ความแตกต่างระหว่าง UTF-8 และ UTF-16 หรือไม่


137

ความแตกต่างระหว่าง UTF-8 และ UTF-16 หรือไม่ ทำไมเราต้องการสิ่งเหล่านี้

MessageDigest md = MessageDigest.getInstance("SHA-256");
String text = "This is some text";

md.update(text.getBytes("UTF-8")); // Change this to "UTF-16" if needed
byte[] digest = md.digest();

2
jon skeet มีบทความที่ดีเกี่ยวกับการเข้ารหัส .... csharpindepth.com/Articles/General/Unicode.aspx
Mitch Wheat

คำตอบ:


284

ฉันเชื่อว่ามีบทความดีๆมากมายเกี่ยวกับเรื่องนี้ในเว็บ แต่นี่เป็นบทสรุปสั้น ๆ

ทั้ง UTF-8 และ UTF-16 เป็นการเข้ารหัสความยาวผันแปรได้ อย่างไรก็ตามใน UTF-8 อักขระอาจมีอย่างน้อย 8 บิตในขณะที่ความยาวของอักขระ UTF-16 เริ่มต้นด้วย 16 บิต

ข้อดีหลักของ UTF-8:

  • อักขระ ASCII พื้นฐานเช่นตัวเลขตัวอักษรละตินโดยไม่มีการเน้นเสียง ฯลฯ ใช้หนึ่งไบต์ซึ่งเหมือนกับการแทน US-ASCII วิธีนี้สตริง US-ASCII ทั้งหมดจะกลายเป็น UTF-8 ที่ถูกต้องซึ่งให้ความเข้ากันได้ดีในหลายกรณี
  • ไม่มีไบต์ว่างซึ่งอนุญาตให้ใช้สตริงที่สิ้นสุดด้วยค่า null ซึ่งจะแนะนำความเข้ากันได้แบบย้อนหลังอย่างมากเช่นกัน
  • UTF-8 ไม่ขึ้นกับการสั่งซื้อไบต์ดังนั้นคุณไม่ต้องกังวลเกี่ยวกับปัญหา Big Endian / Little Endian

ข้อเสีย UTF-8 หลัก:

  • อักขระทั่วไปจำนวนมากมีความยาวต่างกันซึ่งทำให้การจัดทำดัชนีช้าลงโดย codepoint และการคำนวณจำนวน codepoint จะแย่มาก
  • แม้ว่าลำดับไบต์ไม่สำคัญบางครั้ง UTF-8 ยังคงมี BOM (เครื่องหมายคำสั่งไบต์) ซึ่งทำหน้าที่แจ้งเตือนว่าข้อความถูกเข้ารหัสใน UTF-8 และแบ่งความเข้ากันได้กับซอฟต์แวร์ ASCII แม้ว่าข้อความจะมีอักขระ ASCII เท่านั้น . ซอฟต์แวร์ Microsoft (เช่น Notepad) ชอบเพิ่ม BOM เป็น UTF-8 เป็นพิเศษ

ข้อดีหลักของ UTF-16:

  • ตัวอักษร BMP (เครื่องบินพูดได้หลายภาษาพื้นฐาน) รวมถึงละตินซีริลลิกจีนส่วนใหญ่ (PRC ให้การสนับสนุน codepoints นอก BMP บังคับ) ชาวญี่ปุ่นส่วนใหญ่สามารถแสดงด้วย 2 ไบต์ ความเร็วนี้ขึ้นการจัดทำดัชนีและการคำนวณจุดโค้ดนับในกรณีข้อความที่ไม่ได้มีตัวละครเสริม
  • แม้ว่าข้อความนั้นจะมีอักขระเสริม แต่ก็ยังคงแสดงเป็นคู่ค่า 16 บิตซึ่งหมายความว่าความยาวทั้งหมดยังสามารถหารด้วยสองและอนุญาตให้ใช้ 16 บิตcharเป็นองค์ประกอบดั้งเดิมของสตริง

ข้อเสีย UTF-16 หลัก:

  • ไบต์ว่างจำนวนมากในสตริง US-ASCII ซึ่งหมายความว่าไม่มีสตริงที่สิ้นสุดด้วยค่า null และหน่วยความจำที่สิ้นเปลืองจำนวนมาก
  • ใช้เป็นการเข้ารหัสที่มีความยาวคงที่“ ส่วนใหญ่ใช้งานได้” ในสถานการณ์ทั่วไปจำนวนมาก (โดยเฉพาะในสหรัฐอเมริกา / EU / ประเทศที่มีตัวอักษร Cyrillic / อิสราเอล / อาหรับ / อิหร่าน / อิหร่านและอื่น ๆ ) ซึ่งมักจะนำไปสู่การสนับสนุนที่ขาด ซึ่งหมายความว่าโปรแกรมเมอร์จะต้องระวังคู่ตัวแทนและจัดการอย่างถูกต้องในกรณีที่มันสำคัญ!
  • ความยาวแปรผันดังนั้นการนับหรือการทำดัชนี codepoints นั้นมีค่าใช้จ่ายสูงกว่า UTF-8

โดยทั่วไป UTF-16 มักจะดีกว่าสำหรับการแสดงในหน่วยความจำเพราะ BE / LE นั้นไม่เกี่ยวข้อง (เพียงแค่ใช้ลำดับดั้งเดิม) และการจัดทำดัชนีนั้นเร็วกว่า (อย่าลืมจัดการคู่ตัวแทนที่ถูกต้อง) ในทางกลับกัน UTF-8 นั้นดีมากสำหรับไฟล์ข้อความและโปรโตคอลเครือข่ายเนื่องจากไม่มีปัญหา BE / LE และการยกเลิก null มักมีประโยชน์เช่นเดียวกับความเข้ากันได้ของ ASCII


3
การขาดเฉพาะส่วน BE / LE บน UTF16 :) UTF-8 มีข้อเสียอีกประการหนึ่งมันอาจสร้างเอาต์พุตที่ยาวกว่า UTF16
bestsss

4
ใช่ฉันลืมเกี่ยวกับ BE / LE ไม่ใช่เรื่องใหญ่ แต่โดยเฉพาะอย่างยิ่งสำหรับการใช้ในหน่วยความจำ UTF-8 จะสร้างเอาต์พุตนานขึ้นเฉพาะเมื่อมีอักขระสามไบต์เกี่ยวข้อง แต่นั่นหมายถึงจีนและญี่ปุ่นเป็นส่วนใหญ่ ในทางกลับกันหากข้อความมีอักขระ US-ASCII จำนวนมากข้อความนั้นอาจสร้างเอาต์พุตที่สั้นลงดังนั้นไม่ว่าจะเป็นข้อเสียหรือไม่นั้นขึ้นอยู่กับสถานการณ์เฉพาะ
Sergei Tachenov

ฉันไม่ได้คิดว่าจะกล่าวถึงโปรทันทีของ utf-8 ความยาวที่สั้นลง เกี่ยวกับเอาต์พุตที่ยาวขึ้นของ utf-8 ซึ่งเป็น 'อาจ' ด้วยเหตุผล แต่ถ้าเป้าหมายอยู่ไกลออกไปการเข้ารหัสเริ่มต้นควรเป็น utf-16 สำหรับตัวอย่าง md.update (text.getBytes ("UTF-8")); การเข้ารหัสไม่สำคัญเนื่องจากแฮชมีความเสถียรทั้งสองวิธี
bestsss

วิธีที่เร็วที่สุดในการแปลง String เป็นไบต์เป็นสิ่งที่โพสต์ลงในตัวอย่าง
bestsss

คุณพูดว่าตัวละครมีความยาวแตกต่างกันใน UTF-8 ดังนั้นมันจะช้าลงในการจัดทำดัชนีและการคำนวณความยาว แต่ฉันสงสัยว่าตัวละครใน UTF-16 นั้นมีความยาวต่างกันเช่นกันควรทำดัชนีและคำนวณความยาวของ UTF-16
nicky_zs

19

มันเป็นโครงร่างที่แตกต่างกันเพียงสำหรับการแสดงอักขระ Unicode

ทั้งสองเป็นความยาวผันแปร - UTF-16 ใช้ 2 ไบต์สำหรับอักขระทั้งหมดในระนาบหลายภาษาพื้นฐาน (BMP) ซึ่งมีอักขระส่วนใหญ่ที่ใช้งานทั่วไป

UTF-8 ใช้ระหว่าง 1 ถึง 3 ไบต์สำหรับอักขระใน BMP สูงสุด 4 สำหรับอักขระในช่วง Unicode ปัจจุบันที่ U + 0000 ถึง U + 1FFFFF และขยายได้สูงสุด U + 7FFFFFFF หากจำเป็น ... แต่โดยเฉพาะอย่างยิ่งอักขระ ASCII ทั้งหมดจะแสดงเป็นไบต์เดียวละ

สำหรับวัตถุประสงค์ของการแยกย่อยข้อความไม่สำคัญว่าคุณเลือกสิ่งใดตราบใดที่ทุกคนที่พยายามสร้างการแยกย่อยใช้ตัวเลือกเดียวกัน

ดูหน้านี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ UTF-8 และ Unicode

(โปรดทราบว่าอักขระ Java ทั้งหมดเป็นรหัสจุด UTF-16 ภายใน BMP เพื่อแสดงอักขระเหนือ U + FFFF คุณต้องใช้คู่ตัวแทนใน Java)


5

ความปลอดภัย: ใช้เฉพาะ UTF-8

ความแตกต่างระหว่าง UTF-8 และ UTF-16 หรือไม่ ทำไมเราต้องการสิ่งเหล่านี้

มีอยู่อย่างน้อยสองสามช่องโหว่ความปลอดภัยในการใช้งานของUTF-16 ดูวิกิพีเดียเพื่อดูรายละเอียด

WHATWGและW3Cได้ประกาศว่าเป็นUTF-8เท่านั้นที่จะใช้บนเว็บ

ปัญหา [ความปลอดภัย] ที่ระบุไว้หายไปเมื่อใช้ UTF-8 โดยเฉพาะซึ่งเป็นหนึ่งในหลาย ๆ เหตุผลที่ตอนนี้เป็นการเข้ารหัสที่จำเป็นสำหรับทุกสิ่ง

กลุ่มอื่น ๆ กำลังพูดเช่นเดียวกัน

ดังนั้นในขณะที่ UTF-16 อาจยังคงถูกใช้ภายในบางระบบเช่น Java และ Windows การใช้ UTF-16 ที่คุณเคยเห็นในอดีตสำหรับไฟล์ข้อมูลการแลกเปลี่ยนข้อมูลและอื่น ๆ นั้นอาจหายไปโดยสิ้นเชิง


4

สิ่งนี้ไม่เกี่ยวข้องกับ UTF-8/16 (โดยทั่วไปแม้ว่ามันจะแปลงเป็น UTF16 และส่วน BE / LE สามารถตั้งค่าได้ด้วยเส้นเดียว) แต่ด้านล่างเป็นวิธีที่เร็วที่สุดในการแปลงสตริงเป็นไบต์ [] ตัวอย่างเช่น: ดีสำหรับกรณีที่มีให้ (รหัสแฮช) String.getBytes (enc) ค่อนข้างช้า

static byte[] toBytes(String s){
        byte[] b=new byte[s.length()*2];
        ByteBuffer.wrap(b).asCharBuffer().put(s);
        return b;
    }

-2

วิธีง่ายๆในการแยกความแตกต่าง UTF-8 และ UTF-16 คือการระบุความเหมือนกันระหว่างพวกเขา

นอกเหนือจากการแบ่งปันหมายเลขยูนิโค้ดที่เหมือนกันสำหรับตัวละครที่กำหนดแต่ละตัวเป็นรูปแบบของตัวเอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.