1. ما هو Unicode؟
نشأت Unicode من فكرة بسيطة للغاية: لتضمين جميع الشخصيات في العالم في مجموعة. طالما أن الكمبيوتر يدعم مجموعة الأحرف هذه ، فيمكنه عرض جميع الأحرف ، ولن يكون هناك رمز مشتعل مرة أخرى.
يبدأ من 0 ويحدد رقمًا لكل رمز ، والذي يسمى "نقطة codepoint". على سبيل المثال ، يكون رمز نقطة الكود 0 فارغًا (يشير إلى أن جميع البتات الثنائية هي 0).
انسخ الرمز على النحو التالي: U+0000 = NULL
في الصيغة أعلاه ، تعني U+ أن الرقم السداسي عشري التالي مباشرة هو نقطة رمز Unicode.
في الوقت الحاضر ، فإن أحدث إصدار من Unicode هو الإصدار 7.0 ، مع ما مجموعه 109،449 رموز ، منها 74،500 مدرجة في الشخصيات الصينية واليابانية والكورية. يمكن الاعتقاد تقريبًا بأن أكثر من ثلثي الرموز الموجودة في العالم يأتي من شخصيات شرق آسيا. على سبيل المثال ، نقطة الكود لـ "Good" الصينية هي سداسي عشري 597D.
انسخ الرمز كما يلي: u+597d = موافق
مع العديد من الرموز ، لم يتم تعريف Unicode في وقت واحد ، ولكن تعريف القسم. يمكن لكل منطقة تخزين 65،536 (216) حرفًا ، تسمى طائرة. حاليًا ، هناك 17 (25) طائرة في المجموع ، مما يعني أن حجم مجموعة أحرف Unicode بالكامل هو الآن 221.
تسمى بتات أحرف 65536 الأولى المستوى الأساسي (BMP المختصرة) ، وتتراوح نقاط الكود الخاصة بهم من 0 إلى 216-1. مكتوبة في سداسي عشري من U+0000 إلى U+FFFF. يتم وضع جميع الشخصيات الأكثر شيوعًا في هذه الطائرة ، وهي أول مستوى يحدده Unicode ونشره.
يتم وضع الأحرف المتبقية في الطائرة المساعدة (SMP المختصرة) ، وتتراوح نقاط الكود من U+010000 إلى U+10FFFF.
2. UTF-32 و UTF-8
يونيكود يحدد فقط نقطة رمز كل حرف. أي نوع من ترتيب البايت يستخدم لتمثيل نقطة الرمز هذه ، والتي تتضمن طريقة الترميز.
طريقة الترميز الأكثر سهولة هي أن كل نقطة رمز يتم تمثيلها بأربعة بايت ، ومحتوى البايت يتوافق مع نقطة الكود الأولى تلو الأخرى. تسمى طريقة الترميز هذه UTF-32. على سبيل المثال ، يتم تمثيل Code Point 0 بأربعة بايت من 0 ، ويتم إضافة Code Point 597D مع بايتتين من 0 في المقدمة.
انسخ الرمز على النحو التالي: U+0000 = 0x0000 0000U+597D = 0x0000 597D
ميزة UTF-32 هي أن قواعد التحويل بسيطة وبديهية وكفاءة البحث عالية. العيب هو أنه يضيع مساحة ، والنص الإنجليزي مع نفس المحتوى سيكون أكبر أربع مرات من ترميز ASCII. هذا العيب مميت ، مما يؤدي إلى عدم استخدام أي شخص بالفعل طريقة الترميز هذه. ينص معيار HTML5 بشكل صريح على أنه يجب عدم ترميز صفحات الويب في UTF-32.
ما يحتاجه الناس حقًا هو طريقة ترميز لتوفير الفضاء ، مما أدى إلى ولادة UTF-8. UTF-8 هي طريقة ترميز متغيرة طول ، مع أطوال الأحرف تتراوح من 1 بايت إلى 4 بايت. الشخصيات الأكثر شيوعا ، أقصر بايت. يتم تمثيل أول 128 حرفًا ببايت واحد فقط ، وهو بالضبط نفس رمز ASCII.
عدد الأرقام بايت 0x0000 - 0x007F10X0080 - 0x07FF20X0800 - 0xFFF30X010000 - 0x10FFFF4
بسبب ميزة توفير المساحة في UTF-8 ، أصبحت أكثر تشفير الويب شيوعًا على الإنترنت. ومع ذلك ، لا علاقة له بموضوع اليوم ، لذلك لن أذهب إليه. بالنسبة لطريقة الترميز المحددة ، يرجى الرجوع إلى "ملاحظات تشفير الأحرف" التي كتبتها منذ عدة سنوات.
ثالثا. مقدمة إلى UTF-16
يكون ترميز UTF-16 بين UTF-32 و UTF-8 ، ويجمع بين خصائص طريقتين للترميز: الطول الثابت والطول المتغير.
قواعد الترميز الخاصة بها بسيطة: الشخصيات في المستوى الأساسي تشغل 2 بايت ، والأحرف الموجودة في الطائرة المساعدة تشغل 4 بايت. وهذا يعني أن طول ترميز UTF-16 هو إما بايتان (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) ، والتي تسمى بتات منخفضة (ل). هذا يعني أنه يتم تقسيم حرف للطائرة المساعدة إلى طائرتين أساسيتين لتمثيلات الشخصية.
لذلك ، عندما نواجه بايتين ونجد أن نقطة الكود الخاصة بها هي بين U+D800 و U+DBFF ، يمكننا أن نستنتج أن نقطة الكود التي تلي مباشرة البايتين يجب أن تكون بين U+DC00 و U+DFFF. يجب تفسير هذه البايتات الأربعة معًا.
رابعا. UTF-16 Transcoding Formula
عند تحويل رمز Unicode إلى UTF-16 ، قم أولاً بتمييز ما إذا كان هذا حرفًا مسطحًا أساسيًا أو حرفًا مسطحًا إضافيًا. إذا كان هذا هو الأول ، فقم بتحويل نقطة الكود مباشرة إلى النموذج السداسي المقابل ، بطول بايتتين.
انسخ الرمز كما يلي: 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 مجموعات أحرف Unicode ، ولكنها تدعم طريقة ترميز واحدة فقط.
هذا الترميز ليس UTF-16 ، ولا UTF-8 ، ولا UTF-32. لا تستخدم JavaScript طرق الترميز أعلاه.
يستخدم JavaScript UCS-2!
السادس. UCS-2 الترميز
لماذا ظهر UCS-2 فجأة؟ هذا يتطلب القليل من التاريخ.
في العصر الذي لم يظهر فيه الإنترنت بعد ، كان هناك فريقان أراد كلاهما إنشاء مجموعة شخصية موحدة. أحدهما هو فريق Unicode الذي أنشئ في عام 1988 ، والآخر هو فريق UCS الذي أنشئ في عام 1989. عندما اكتشفوا وجود بعضهم البعض ، توصلوا بسرعة إلى اتفاق: ليست هناك حاجة لمجموعتين من الشخصيات الموحدة في العالم.
في أكتوبر 1991 ، قرر الفريقان دمج مجموعة الشخصيات. وبعبارة أخرى ، سيتم إصدار مجموعة واحدة فقط من مجموعات الأحرف من الآن فصاعدًا ، وهو Unicode ، وسيتم مراجعة مجموعات الأحرف التي تم إصدارها مسبقًا ، وستكون نقاط رمز UCS هي نفسها تمامًا مثل Unicode.
تقدم تطوير UCS أسرع من Unicode. في عام 1990 ، تم الإعلان عن أول طريقة ترميز UCS-2 ، باستخدام 2 بايت لتمثيل الأحرف مع نقاط رمز. (في ذلك الوقت ، لم يكن هناك سوى طائرة واحدة ، والتي كانت الطائرة الأساسية ، لذلك كان 2 بايت كافية.) لم يتم الإعلان عن ترميز UTF-16 حتى يوليو 1996 ، وقد تم الإعلان بوضوح عن أنها كانت مجموعة من UCS-2 ، أي أن أحرف الطائرة الأساسية قد تم ترميزها بواسطة UCS-2 ، ومحدد شخصيات الطائرة المخصصة 4-byte.
ببساطة ، العلاقة بين الاثنين هي أن UTF-16 يحل محل UCS-2 ، أو تم دمج UCS-2 في UTF-16. لذلك ، الآن لا يوجد سوى UTF-16 ، لا UCS-2.
7. خلفية ميلاد جافا سكريبت
لذا ، لماذا لا تختار JavaScript UTF-16 الأكثر تقدماً ، ولكنه يستخدم UCS-2 عفا عليه الزمن بالفعل؟
الإجابة بسيطة للغاية: ليس الأمر أنك لا تريد ذلك ، فهو لا يمكنك ذلك. لأنه عندما ظهرت لغة JavaScript ، لم يكن هناك ترميز UTF-16.
في مايو 1995 ، صمم Brendan Eich لغة JavaScript في 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 ، والحرف الأول الذي تم الحصول عليه هو حرف فارغ ، ونقطة رمز الحرف الأول الذي تم الحصول عليه هو 0xDB34. هذه النتائج ليست صحيحة!
لحل هذه المشكلة ، يجب عليك إصدار حكم على نقطة المدونة ثم ضبطها يدويًا. فيما يلي الطريقة الصحيحة لكتابة سلسلة.
انسخ رمز الرمز كما يلي: بينما (++ فهرس <الطول) {// ... if (charcode> = 0xd800 && charcode <= 0xdbff) {output.push (حرف+string.charat (++ index)) ؛ } آخر {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 مباشرةً مع نقاط الكود ، والتي تتم كتابتها باسم "نقاط رمز + U + Backslash".
انسخ الرمز كما يلي: 'ok' === '/u597d' // true
ومع ذلك ، فإن هذا التدوين غير صالح لنقاط الرمز 4 بايت. قام ES6 بإصلاح هذه المشكلة ويمكنه تحديدها بشكل صحيح طالما تم وضع نقاط الكود في أقواس مجعد.
(3) وظيفة معالجة السلسلة
أضافت ES6 العديد من الوظائف الجديدة التي تتعامل بشكل خاص مع نقاط الرمز 4 بايت.
String.FromCodePoint (): إرجاع الحرف المقابل من نقطة رمز Unicode
string.prototype.codepointat (): إرجاع نقطة الكود المقابلة من الحرف
string.prototype.at (): إرجاع الحرف في الموضع المحدد للسلسلة
(4) تعبير منتظم
يوفر ES6 معدل U لإضافة نقاط رمز 4 بايت إلى تعبيرات منتظمة.
(5) تنظيم يونيكود
بعض الأحرف لديها رموز إضافية بالإضافة إلى الحروف. على سبيل المثال ، في Pinyin الصينية ، النغمة على الرسالة هي رمز إضافي. تعتبر رموز النغمة مهمة جدًا للعديد من اللغات الأوروبية.
يونيكود يوفر طريقتين تمثيل. واحد هو حرف واحد مع رموز إضافية ، أي نقطة رمز تمثل حرفًا ، مثل نقطة رمز ǒ هي U+01d1 ؛ والآخر هو استخدام الرمز الإضافي كنقطة رمز وعرضه بالاشتراك مع الحرف الرئيسي ، أي أن نقطتي رمز تمثل حرفًا ، مثل ǒ يمكن كتابتها كـ O (U+004F)+ˇ (U+030C).
انسخ الرمز كما يلي: // الطريقة 1 '/u01d1' // 'ǒ' // method 2 '/u004f/u030c' // 'ǒ'
هذان طريقتان التمثيل هما نفسهان بالضبط في الرؤية والدلالات ويجب معاملته كمواقف مكافئة. ومع ذلك ، لا يمكن لـ JavaScript معرفة.
انسخ الرمز كما يلي: '/u01d1' === '/u004f/u030c' // false
يوفر ES6 طريقة تطبيع ، مما يسمح "بتنظيم Unicode" ، أي تحويل الطريقتين إلى نفس التسلسل.
انسخ الرمز كما يلي: '/U01D1'.Normalial () ===' /U004F/U030C'.NORMALISION () // TRUE
لمزيد من المقدمة إلى ES6 ، يرجى الاطلاع على "Entertainment of Ecmascript 6".