ทำไม UTF-8 ถึงเสียหลายบิตในการเข้ารหัส


17

ตามบทความ Wikipedia , UTF-8 มีรูปแบบนี้:

รหัสแรกรหัสล่าสุดไบต์ไบต์ 1 ไบต์ 2 ไบต์ 3 ไบต์ 4
จุดจุดที่ใช้
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 07FF 2 110xxxxx 10xxxxxx
U + 0800 U + FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
U + 10000 U + 1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxxx
x หมายถึงบิตนี้ใช้เพื่อเลือกจุดรหัส

สิ่งนี้จะสูญเสียสองบิตในแต่ละไบต์การต่อเนื่องและหนึ่งบิตในไบต์แรก ทำไม UTF-8 ถึงไม่เข้ารหัสดังต่อไปนี้

รหัสแรกรหัสล่าสุดไบต์ไบต์ 1 ไบต์ 2 ไบต์ 3
จุดจุดที่ใช้
U + 0000 U + 007F 1 0xxxxxxx
U + 0080 U + 3FFF 2 10xxxxxx xxxxxxxx
U + 0800 U + 1FFFFF 3 110xxxxx xxxxxxxx xxxxxxxx

มันจะประหยัดหนึ่งไบต์เมื่อจุดโค้ดอยู่นอกระนาบ Multilingual แบบพื้นฐานหรือถ้าจุดรหัสอยู่ในช่วง [U + 800, U + 3FFF]

ทำไม UTF-8 ถึงไม่เข้ารหัสอย่างมีประสิทธิภาพมากกว่า?


3
cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt การเข้ารหัสที่เสนอของคุณนั้นคล้ายคลึงกับข้อเสนอ FSS / UTF ดั้งเดิม Ken Thompson และ Rob Pike ต้องการคุณสมบัติการซิงโครไนซ์ด้วยตนเอง
ninjalj

4
นอกจากนี้การเข้ารหัสของคุณดูเหมือนจะไม่รับประกันว่าค่ารหัส ASCII จะไม่ปรากฏในส่วนใดส่วนหนึ่งของการแสดงอักขระที่ไม่ใช่ ASCII FSS / UTF และ UTF-8 ได้รับการออกแบบให้ทำงานกับโปรแกรมรุ่นเก่า (เช่น: ที่ใช้ ASCII NUL และ slash (ตัวคั่นเส้นทาง) เป็นตัวคั่น)
ninjalj

คำตอบ:


26

สิ่งนี้จะทำเพื่อให้คุณสามารถตรวจจับเมื่อคุณอยู่ในช่วงกลางของลำดับหลายไบต์ เมื่อมองไปที่ UTF-8 ข้อมูลคุณรู้ว่าถ้าคุณเห็น10xxxxxxว่าคุณอยู่ในกลางของตัวอักษรสัญลักษณ์ที่และควรสำรองในกระแสจนกว่าคุณจะเห็นอย่างใดอย่างหนึ่งหรือ0xxxxxx 11xxxxxxใช้รูปแบบของคุณไบต์ 2 หรือ 3 สามารถท้ายด้วย patters อย่างใดอย่างหนึ่ง0xxxxxxxหรือ11xxxxxx

โปรดทราบว่าจำนวนการบันทึกจะแตกต่างกันไปตามประเภทของข้อมูลสตริงที่คุณเข้ารหัส สำหรับข้อความส่วนใหญ่แม้แต่ข้อความในเอเชียคุณจะไม่ค่อยเห็นอักขระสี่ไบต์พร้อมข้อความปกติ นอกจากนี้การประเมินที่ไร้เดียงสาของผู้คนเกี่ยวกับลักษณะของข้อความที่มักจะผิด ฉันมีข้อความที่แปลเป็นภาษาท้องถิ่นสำหรับ UTF-8 ซึ่งรวมถึงสายอักขระภาษาญี่ปุ่นจีนและเกาหลี แต่จริง ๆ แล้วเป็นภาษารัสเซียที่ใช้พื้นที่มากที่สุด (เพราะสตริงเอเชียของเรามักจะมีตัวอักษรโรมันสลับกันสำหรับชื่อที่เหมาะสมเครื่องหมายวรรคตอนและเช่นนั้นและเพราะคำภาษาจีนโดยเฉลี่ยคือ 1-3 ตัวอักษรในขณะที่คำรัสเซียเฉลี่ยมีอีกมากมาย)


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

11
แน่ใจ แบบแผนของคุณมีความหนาแน่นของข้อมูลมากขึ้น แต่ไม่มีคุณสมบัติที่สำคัญให้ UTF-8 โดยทั่วไปแล้วคนเราชอบความปลอดภัยซึ่งเป็นสาเหตุที่ทำให้ UTF-8 เป็นไปได้ นอกจากนี้เพื่อพิสูจน์ว่ารูปแบบของคุณมีประสิทธิภาพมากขึ้นจริง ๆ คุณต้องแสดงสถิติโดยใช้ข้อความจริง คุณอาจพบว่าในข้อความจริงส่วนใหญ่รูปแบบของคุณจะบันทึกจำนวนเล็กน้อยมากและการออมจึงไม่คุ้มค่า
Gort the Robot

3
คุณลักษณะสำคัญอีกอย่างหนึ่ง: หากไม่มี codepoint เป็นศูนย์ฝังตัวอยู่จะไม่มีเลขศูนย์ฝังอยู่ในสตริง
Deduplicator

สำหรับสคริปต์ภาษาไทยคุณต้องอนุญาต 4 ไบต์ต่ออักขระที่พิมพ์ พวกเขาไม่เพียง แต่มางานปาร์ตี้ช้าและได้กลุ่มรหัสที่มีตัวเลขสูง หลายสิ่งหลายอย่างที่ดูเหมือนอักขระตัวเดียวเมื่อพิมพ์จะประกอบด้วยอักขระยูนิโค้ดแตกต่างกันสามตัว
James Anderson

@ qbt937: ใช้แบบแผนของคุณคนจะสแกนอย่างรวดเร็วเพื่อค้นหาว่าสตริงหนึ่งมีอีกหรือไม่
supercat

6

วิธีที่ช่วยให้อย่างเป็นทางการถอดรหัสรู้เมื่อมันอยู่ตรงกลางของ tuple และมันรู้ที่จะข้ามไบต์ (หรือย้อนกลับไป) จนกระทั่งไบต์เริ่มต้นด้วย0หรือ11; สิ่งนี้จะป้องกันค่าขยะเมื่อไบต์เดียวเสียหาย


3

คำตอบสั้น ๆ ข้อเสนอของคุณไม่ได้แยกความแตกต่างระหว่างไบต์แรกและไบต์ต่อเนื่อง

รูปแบบบิตที่ส่วนท้ายสุดของไบต์แรกจะบอกให้คุณทราบถึงจำนวนไบต์ที่อักขระตัวจริงถูกสร้างขึ้น รูปแบบเหล่านี้ยังมีการรับรู้ข้อผิดพลาดบางอย่างขณะแยกวิเคราะห์สตริง หากคุณกำลังอ่านไบต์แรก (ดูเหมือน) ของอักขระและคุณได้รับ 10xxxxxx แสดงว่าคุณรู้ว่าคุณไม่ได้ซิงค์


2

สิ่งที่ไม่ได้กล่าวถึงคือถ้าคุณมีลำดับของจุดรหัสที่ถูกต้องและตัวชี้ที่รับประกันว่าจะชี้ไปที่ไบต์แรกของจุดรหัสด้วย UTF-8 คุณสามารถค้นหาตัวชี้ไปยังไบต์แรกได้อย่างง่ายดายมาก ของจุดรหัสก่อนหน้า (ข้ามไบต์ทั้งหมดที่ขึ้นต้นด้วย 01xxxxx) ด้วยการเข้ารหัสของคุณมันเป็นไปไม่ได้เลยหากไม่มีการตรวจสอบไบต์ทั้งหมดจนถึงจุดเริ่มต้นของสตริง

พิจารณาลำดับของ (2n + 2) ไบต์

0xxxxxxx
n times (10xxxxxx, 10xxxxxx)
0xxxxxxx

และ

n times (10xxxxxx, 10xxxxxx)
(10xxxxxx, 0xxxxxxx)

ถ้าคุณมีตัวชี้ไปยังไบต์แรกของจุดรหัสแรกหลังจากลำดับนี้คุณต้องตรวจสอบไบต์ทั้งหมดเพื่อค้นหาว่า codepoint ล่าสุดคือ 0xxxxxxx หรือ (10xxxxxx, 0xxxxxxx)

มีรูปแบบการเข้ารหัสที่มีประสิทธิภาพมากขึ้นซึ่งการไปยังจุดรหัสก่อนหน้าสามารถทำได้ในเวลาที่คงที่และตัวชี้ไปยังจุดกึ่งกลางของจุดรหัสสามารถแก้ไขได้ อนุญาตรหัสต่อไปนี้:

X where X < 128
YX where 128 ≤ Y < 236, X < 128
ZYY where 236 ≤ Z < 256, 0 ≤ Y < 236. 

หากหนึ่งในสามไบต์ก่อนหน้านี้คือ≥ 236 แสดงว่าเป็นการเริ่มต้นของลำดับ 3 ไบต์เนื่องจากอาจไม่มีสองไบต์ดังกล่าวภายในลำดับ 3 ไบต์ใด ๆ ที่ถูกต้อง มิฉะนั้นหากหนึ่งในสองไบต์ก่อนหน้านี้คือ≥ 128 ก็จะเป็นการเริ่มต้นของลำดับสองไบต์ มิฉะนั้นไบต์ก่อนหน้านี้เป็นไบต์เดียว <128

การค้นหาสตริงย่อยจะยากขึ้นเล็กน้อย คุณอาจต้องการยกเว้นศูนย์ไบต์เพื่อให้สตริงมีศูนย์ไบต์เท่านั้นถ้าประกอบด้วยจุดรหัสศูนย์


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