1. Unicode คืออะไร?
Unicode มีต้นกำเนิดมาจากความคิดที่ง่ายมาก: เพื่อรวมตัวละครทั้งหมดในโลกไว้ในคอลเล็กชั่น ตราบใดที่คอมพิวเตอร์รองรับชุดอักขระนี้ก็สามารถแสดงอักขระทั้งหมดได้และจะไม่มีรหัสที่อ่านไม่ออกอีกครั้ง
มันเริ่มต้นที่ 0 และระบุตัวเลขสำหรับแต่ละสัญลักษณ์ซึ่งเรียกว่า "codepoint" ตัวอย่างเช่นสัญลักษณ์ของโค้ดจุด 0 เป็นโมฆะ (ระบุว่าบิตไบนารีทั้งหมดคือ 0)
คัดลอกรหัสดังนี้: U+0000 = NULL
ในสูตรข้างต้น U+ หมายความว่าหมายเลขเลขฐานสิบหกทันทีต่อไปนี้เป็นจุดรหัสของ Unicode
ในปัจจุบัน Unicode เวอร์ชันล่าสุดคือเวอร์ชัน 7.0 โดยมีสัญลักษณ์ทั้งหมด 109,449 สัญลักษณ์ซึ่งรวมอยู่ในตัวละครจีนญี่ปุ่นและเกาหลี สามารถเชื่อได้ว่ามากกว่าสองในสามของสัญลักษณ์ที่มีอยู่ในโลกมาจากตัวละครเอเชียตะวันออก ตัวอย่างเช่นจุดรหัสสำหรับภาษาจีน "ดี" คือเลขฐานสิบหก 597D
คัดลอกรหัสดังนี้: U+597D = ตกลง
ด้วยสัญลักษณ์มากมาย Unicode ไม่ได้ถูกกำหนดในครั้งเดียว แต่เป็นคำจำกัดความของพาร์ติชัน แต่ละพื้นที่สามารถเก็บอักขระ 65,536 (216) เรียกว่าเครื่องบิน ปัจจุบันมีเครื่องบินทั้งหมด 17 (25) ลำซึ่งหมายความว่าขนาดของชุดอักขระ Unicode ทั้งหมดคือ 221
บิตตัวละคร 65536 ตัวแรกเรียกว่าระนาบพื้นฐาน (ตัวย่อ BMP) และคะแนนรหัสของพวกเขาอยู่ในช่วงตั้งแต่ 0 ถึง 216-1 เขียนใน Hexadecimal มาจาก U+0000 ถึง U+FFFF ตัวละครที่พบบ่อยที่สุดทั้งหมดอยู่ในระนาบนี้ซึ่งเป็นระนาบแรกที่ Unicode กำหนดและเผยแพร่
อักขระที่เหลือจะถูกวางไว้ในระนาบเสริม (ตัวย่อ SMP) และคะแนนรหัสอยู่ในช่วงตั้งแต่ U+010000 ถึง U+10FFFF
2. UTF-32 และ UTF-8
Unicode ระบุจุดรหัสของแต่ละอักขระเท่านั้น คำสั่งไบต์ชนิดใดที่ใช้เพื่อแสดงจุดรหัสนี้ซึ่งเกี่ยวข้องกับวิธีการเข้ารหัส
วิธีการเข้ารหัสที่ใช้งานง่ายที่สุดคือแต่ละจุดโค้ดจะถูกแทนด้วยสี่ไบต์และเนื้อหาไบต์สอดคล้องกับจุดรหัสหนึ่งทีละหนึ่ง วิธีการเข้ารหัสนี้เรียกว่า UTF-32 ตัวอย่างเช่นรหัสจุด 0 แสดงโดยสี่ไบต์ของ 0 และรหัสจุด 597D จะถูกเพิ่มด้วยสองไบต์ของ 0 ด้านหน้า
คัดลอกรหัสดังนี้: U+0000 = 0x0000 0000U+597D = 0x0000 597D
ข้อได้เปรียบของ UTF-32 คือกฎการแปลงนั้นง่ายและใช้งานง่ายและประสิทธิภาพการค้นหาสูง ข้อเสียคือมันเป็นพื้นที่ที่สูญเปล่าและข้อความภาษาอังกฤษที่มีเนื้อหาเดียวกันจะมีขนาดใหญ่กว่าการเข้ารหัส ASCII สี่เท่า ข้อเสียนี้เป็นอันตรายถึงชีวิตทำให้ไม่มีใครใช้วิธีการเข้ารหัสนี้ มาตรฐาน HTML5 กำหนดอย่างชัดเจนว่าหน้าเว็บต้องไม่ถูกเข้ารหัสเป็น UTF-32
สิ่งที่ผู้คนต้องการจริงๆคือวิธีการเข้ารหัสการประหยัดพื้นที่ซึ่งนำไปสู่การเกิดของ UTF-8 UTF-8 เป็นวิธีการเข้ารหัสความยาวตัวแปรที่มีความยาวอักขระตั้งแต่ 1 ไบต์ถึง 4 ไบต์ ยิ่งอักขระที่ใช้กันทั่วไปมากเท่าไหร่ไบต์ก็จะสั้นลง อักขระ 128 ตัวแรกแสดงโดยเพียง 1 ไบต์ซึ่งเหมือนกับรหัส ASCII
ช่วงจำนวนไบต์ 0x0000 - 0x007f10x0080 - 0x07ff20x0800 - 0xfff30x010000 - 0x10ffff4
เนื่องจากคุณสมบัติการประหยัดพื้นที่ของ UTF-8 จึงได้กลายเป็นเว็บเข้ารหัสที่พบบ่อยที่สุดบนอินเทอร์เน็ต อย่างไรก็ตามมันมีส่วนเกี่ยวข้องกับหัวข้อของวันนี้เพียงเล็กน้อยดังนั้นฉันจะไม่เข้าไป สำหรับวิธีการแปลงรหัสเฉพาะโปรดดู "บันทึกการเข้ารหัสอักขระ" ฉันเขียนเมื่อหลายปีก่อน
iii. บทนำสู่ UTF-16
การเข้ารหัส UTF-16 อยู่ระหว่าง UTF-32 และ UTF-8 และรวมคุณสมบัติของวิธีการเข้ารหัสสองวิธี: ความยาวคงที่และความยาวตัวแปร
กฎการเข้ารหัสนั้นง่าย: ตัวละครในระนาบพื้นฐานครอบครอง 2 ไบต์และตัวละครในระนาบเสริมครอบครอง 4 ไบต์ กล่าวคือความยาวการเข้ารหัสของ UTF-16 คือ 2 ไบต์ (U+0000 ถึง U+FFFF) หรือ 4 ไบต์ (U+010000 ถึง U+10FFFF)
ดังนั้นมีคำถาม: เมื่อเราพบสองไบต์เราจะเห็นว่ามันเป็นตัวละครของตัวเองหรือเราจำเป็นต้องตีความมันกับอีกสองไบต์?
มันฉลาดมากและฉันไม่รู้ว่ามันเป็นความตั้งใจหรือไม่ ในระนาบพื้นฐานจาก U+D800 ถึง U+DFFF เป็นเซ็กเมนต์ที่ว่างเปล่านั่นคือจุดรหัสเหล่านี้ไม่สอดคล้องกับอักขระใด ๆ ดังนั้นเซ็กเมนต์ที่ว่างเปล่านี้สามารถใช้ในการแมปอักขระของระนาบเสริม
โดยเฉพาะอย่างยิ่งมีบิตตัวละคร 220 ตัวในระนาบเสริมซึ่งหมายความว่าต้องใช้บิตไบนารีอย่างน้อย 20 บิตสำหรับตัวละครเหล่านี้ UTF-16 แยก 20 บิตเหล่านี้ออกไปครึ่งหนึ่ง 10 บิตแรกถูกแมปใน U+D800 ถึง U+DBFF (ขนาดพื้นที่ 210) ซึ่งเรียกว่าบิตสูง (H) และ 10 บิตสุดท้ายถูกแมปใน U+DC00 ถึง U+DFFF (ขนาดพื้นที่ 210) ซึ่งเรียกว่าบิตต่ำ (l) ซึ่งหมายความว่าตัวละครของระนาบเสริมจะถูกแบ่งออกเป็นสองระนาบพื้นฐานของการแสดงตัวละคร
ดังนั้นเมื่อเราพบสองไบต์และพบว่าจุดรหัสของมันอยู่ระหว่าง U+D800 และ U+DBFF เราสามารถสรุปได้ว่าจุดรหัสทันทีหลังจากสองไบต์ควรอยู่ระหว่าง U+DC00 และ U+DFFF สี่ไบต์เหล่านี้จะต้องตีความด้วยกัน
iv. สูตรการแปลงรหัส UTF-16
เมื่อแปลงรหัส Unicode ชี้ไปที่ UTF-16 ก่อนอื่นให้แยกแยะว่านี่เป็นอักขระแบนพื้นฐานหรืออักขระแบนเสริม หากเป็นอดีตให้แปลงรหัสจุดโดยตรงเป็นรูปแบบ hexadecimal ที่สอดคล้องกันโดยมีความยาวสองไบต์
คัดลอกรหัสดังนี้: U+597D = 0x597D
หากเป็นอักขระแบนเสริม Unicode เวอร์ชัน 3.0 จะให้สูตรการแปลงรหัส
คัดลอกรหัสรหัสดังต่อไปนี้: h = math.floor ((C -0x10000) / 0x400) + 0xd800l = (C - 0x10000) % 0x400 + 0xdc00
นำตัวละครเป็นตัวอย่างมันเป็นอักขระระนาบเสริมที่มีจุดรหัส U+1D306 กระบวนการคำนวณของการแปลงเป็น UTF-16 มีดังนี้
คัดลอกรหัสรหัสดังต่อไปนี้: h = math.floor ((0x1d306-0x10000)/0x400)+0xd800 = 0xd834l = (0x1d306-0x10000) % 0x400+0xdc00 = 0xdf06
ดังนั้นการเข้ารหัส UTF-16 ของตัวละครคือ 0xd834 DF06 ที่มีความยาวสี่ไบต์
5. การเข้ารหัสใดที่ใช้ใน JavaScript?
ภาษา JavaScript ใช้ชุดอักขระ Unicode แต่รองรับวิธีการเข้ารหัสเพียงวิธีเดียวเท่านั้น
การเข้ารหัสนี้ไม่ใช่ UTF-16 หรือ UTF-8 หรือ UTF-32 JavaScript ไม่ได้ใช้วิธีการเข้ารหัสข้างต้น
JavaScript ใช้ UCS-2!
VI. การเข้ารหัส UCS-2
ทำไม UCS-2 ถึงปรากฏขึ้นทันที? สิ่งนี้ต้องใช้ประวัติเล็กน้อย
ในยุคที่อินเทอร์เน็ตยังไม่ปรากฏขึ้นมีสองทีมที่ทั้งคู่ต้องการสร้างชุดตัวละครแบบครบวงจร หนึ่งคือทีม Unicode ที่ก่อตั้งขึ้นในปี 1988 และอีกทีมคือทีม UCS ที่จัดตั้งขึ้นในปี 1989 เมื่อพวกเขาค้นพบการดำรงอยู่ของกันและกันพวกเขาบรรลุข้อตกลงอย่างรวดเร็ว: ไม่จำเป็นต้องมีตัวละครสองชุดในโลก
ในเดือนตุลาคม 2534 ทั้งสองทีมตัดสินใจรวมชุดตัวละครเข้าด้วยกัน กล่าวอีกนัยหนึ่งจะมีชุดอักขระเพียงชุดเดียวเท่านั้นที่จะถูกปล่อยออกมาจากตอนนี้ซึ่งเป็น Unicode และชุดอักขระที่เปิดตัวก่อนหน้านี้จะได้รับการแก้ไขและจุดรหัสของ UCS จะเหมือนกับ Unicode
ความคืบหน้าการพัฒนาของ UCS นั้นเร็วกว่า Unicode ในปี 1990 มีการประกาศวิธีการเข้ารหัส UCS-2 ครั้งแรกโดยใช้ 2 ไบต์เพื่อแสดงถึงอักขระที่มีจุดรหัส (ในเวลานั้นมีเครื่องบินเพียงลำเดียวซึ่งเป็นเครื่องบินพื้นฐานดังนั้น 2 ไบต์จึงเพียงพอแล้ว) การเข้ารหัส UTF-16 ไม่ได้ประกาศจนถึงเดือนกรกฎาคม 2539 และมีการประกาศอย่างชัดเจนว่ามันเป็นตัวละคร UCS-2 ซึ่งเป็นตัวละครพื้นฐาน
พูดง่ายๆคือความสัมพันธ์ระหว่างทั้งสองคือ UTF-16 แทนที่ UCS-2 หรือ UCS-2 ถูกรวมเข้ากับ UTF-16 ดังนั้นตอนนี้มี UTF-16 เท่านั้นไม่มี UCS-2
7. พื้นหลังการเกิดของ JavaScript
ดังนั้นทำไม JavaScript ไม่เลือก UTF-16 ขั้นสูง แต่ใช้ UCS-2 ที่ล้าสมัยแล้ว?
คำตอบนั้นง่ายมาก: ไม่ใช่ว่าคุณไม่ต้องการนั่นคือสิ่งที่คุณไม่สามารถทำได้ เพราะเมื่อภาษาจาวาสคริปต์ปรากฏขึ้นไม่มีการเข้ารหัส UTF-16
ในเดือนพฤษภาคม 2538 เบรนแดนไอชออกแบบภาษาจาวาสคริปต์ใน 10 วัน ในเดือนตุลาคมเอ็นจิ้นคำอธิบายแรกได้รับการปล่อยตัว; ในเดือนพฤศจิกายนของปีถัดไป NetScape ส่งมาตรฐานภาษาอย่างเป็นทางการไปยัง ECMA (ดู "การกำเนิดของ JavaScript" สำหรับรายละเอียดเกี่ยวกับกระบวนการทั้งหมด) เมื่อเปรียบเทียบวันที่วางจำหน่ายของ UTF-16 (กรกฎาคม 1996) คุณจะเข้าใจว่า NetScape ไม่มีทางเลือกอื่นในเวลานั้นมีเพียงวิธีการเข้ารหัส UCS-2 เท่านั้น!
8. ข้อ จำกัด ของฟังก์ชันอักขระ JavaScript
เนื่องจาก JavaScript สามารถจัดการการเข้ารหัส UCS-2 ได้เท่านั้นอักขระทั้งหมดเป็น 2 ไบต์ในภาษานี้ หากพวกเขาเป็น 4 ไบต์พวกเขาจะได้รับการปฏิบัติเป็นสองไบต์สองครั้ง ฟังก์ชั่นอักขระของ JavaScript ได้รับผลกระทบจากสิ่งนี้และไม่สามารถส่งคืนผลลัพธ์ที่ถูกต้องได้
นำอักขระเป็นตัวอย่างการเข้ารหัส UTF-16 คือ 4 ไบต์ 0xd834df06 ปัญหาคือการเข้ารหัส 4 ไบต์ไม่ได้เป็นของ UCS-2 และ JavaScript ไม่รู้จักและจะถือว่าเป็นอักขระสองตัวแยกกัน U+D834 และ U+DF06 ดังที่ได้กล่าวไว้ก่อนหน้านี้จุดรหัสทั้งสองนี้ว่างเปล่าดังนั้น JavaScript จะพิจารณาว่าพวกเขาเป็นสตริงที่ประกอบด้วยอักขระที่ว่างเปล่าสองตัว!
รหัสข้างต้นระบุว่า JavaScript เชื่อว่าความยาวของอักขระคือ 2 อักขระตัวแรกที่ได้รับคืออักขระ NULL และจุดรหัสของอักขระตัวแรกที่ได้รับคือ 0xDB34 ผลลัพธ์เหล่านี้ไม่ถูกต้อง!
ในการแก้ปัญหานี้คุณต้องตัดสินเกี่ยวกับจุดรหัสแล้วปรับด้วยตนเอง ต่อไปนี้เป็นวิธีที่ถูกต้องในการเขียนสตริง
คัดลอกรหัสรหัสดังนี้: ในขณะที่ (++ ดัชนี <ความยาว) {// ... ถ้า (charcode> = 0xd800 && charcode <= 0xdbff) {output.push (อักขระ+string.charat (++ ดัชนี)); } else {output.push (อักขระ); -
รหัสข้างต้นระบุว่าเมื่อข้ามสตริงคุณจะต้องตัดสินในจุดรหัส ตราบใดที่มันอยู่ในช่วงเวลาระหว่าง 0xd800 ถึง 0xdbff จะต้องอ่านพร้อมกับ 2 ไบต์ถัดไป
ปัญหาที่คล้ายกันมีอยู่ในฟังก์ชั่นการจัดการอักขระ JavaScript ทั้งหมด
String.prototype.replace ()
string.prototype.substring ()
string.prototype.slice ()
-
ฟังก์ชั่นทั้งหมดข้างต้นใช้ได้สำหรับคะแนนรหัส 2 ไบต์เท่านั้น ในการประมวลผลโค้ด 4 ไบต์อย่างถูกต้องคุณต้องปรับใช้เวอร์ชันหนึ่งของคุณเองทีละหนึ่งและตัดสินช่วงรหัสจุดของอักขระปัจจุบัน
9. ECMASCRIPT 6
JavaScript รุ่นต่อไป ECMASCRIPT 6 (ES6 สำหรับระยะสั้น) ได้เพิ่มการสนับสนุน Unicode อย่างมากโดยทั่วไปการแก้ปัญหานี้
(1) ระบุอักขระอย่างถูกต้อง
ES6 สามารถรับรู้คะแนนรหัส 4 ไบต์โดยอัตโนมัติ ดังนั้นจึงง่ายกว่าที่จะสำรวจสตริง
คัดลอกรหัสดังต่อไปนี้: สำหรับ (ให้ S ของสตริง) {// ... }
อย่างไรก็ตามเพื่อรักษาความเข้ากันได้แอตทริบิวต์ความยาวยังคงเป็นพฤติกรรมดั้งเดิม เพื่อให้ได้ความยาวที่ถูกต้องของสตริงคุณสามารถใช้วิธีการต่อไปนี้
คัดลอกรหัสดังนี้: array.from (สตริง). ความยาว
(2) การแสดงจุดรหัส
JavaScript อนุญาตให้อักขระ Unicode แสดงโดยตรงกับจุดรหัสซึ่งเขียนเป็น "Backslash + U + Code Points"
คัดลอกรหัสดังนี้: 'ตกลง' === '/u597d' // true
อย่างไรก็ตามสัญกรณ์นี้ไม่ถูกต้องสำหรับคะแนนรหัส 4 ไบต์ ES6 แก้ไขปัญหานี้และสามารถระบุได้อย่างถูกต้องตราบใดที่จุดรหัสถูกวางไว้ในการจัดฟันแบบหยิก
(3) ฟังก์ชั่นการประมวลผลสตริง
ES6 ได้เพิ่มฟังก์ชั่นใหม่หลายอย่างที่จัดการกับคะแนนรหัส 4 ไบต์โดยเฉพาะ
String.FromCodePoint (): ส่งคืนอักขระที่เกี่ยวข้องจากจุดรหัส Unicode
string.prototype.codepointat (): ส่งคืนจุดรหัสที่เกี่ยวข้องจากอักขระ
string.prototype.at (): ส่งคืนอักขระที่ตำแหน่งที่กำหนดของสตริง
(4) การแสดงออกปกติ
ES6 จัดเตรียมตัวดัดแปลง U เพื่อเพิ่มคะแนนรหัส 4 ไบต์ลงในนิพจน์ทั่วไป
(5) การทำให้เป็นมาตรฐานของ Unicode
อักขระบางตัวมีสัญลักษณ์เพิ่มเติมนอกเหนือจากตัวอักษร ตัวอย่างเช่นในพินอินจีนน้ำเสียงในตัวอักษรเป็นสัญลักษณ์เพิ่มเติม สัญลักษณ์น้ำเสียงมีความสำคัญมากสำหรับภาษายุโรปหลายภาษา
Unicode มีวิธีการแทนสองวิธี หนึ่งคืออักขระตัวเดียวที่มีสัญลักษณ์เพิ่มเติมนั่นคือจุดรหัสแสดงถึงอักขระเช่นจุดรหัสของǒคือ U+01D1; อีกอย่างคือการใช้สัญลักษณ์เพิ่มเติมเป็นจุดรหัสและแสดงร่วมกับอักขระหลักนั่นคือสองจุดรหัสแสดงถึงอักขระเช่นǒสามารถเขียนเป็น O (U+004F)+ˇ (U+030C)
คัดลอกรหัสดังต่อไปนี้: // วิธีการ 1 '/u01d1' // 'ǒ' // เมธอด 2 '/u004f/u030c' // 'ǒ'
วิธีการเป็นตัวแทนทั้งสองนี้มีความเหมือนกันในการมองเห็นและความหมายและควรได้รับการปฏิบัติเป็นสถานการณ์ที่เทียบเท่า อย่างไรก็ตาม JavaScript ไม่สามารถบอกได้
คัดลอกรหัสดังนี้: '/u01d1' === '/u004f/u030c' // false
ES6 จัดเตรียมวิธีการปกติให้ "การทำให้เป็นมาตรฐาน Unicode" นั่นคือการแปลงทั้งสองวิธีเป็นลำดับเดียวกัน
คัดลอกรหัสดังนี้: '/u01d1'.normalize () ===' /u004f/u030c'.normalize () // true
สำหรับการแนะนำเพิ่มเติมเกี่ยวกับ ES6 โปรดดู "Entertainment of Ecmascript 6"