هل سمعت عن تطبيع يونيكود؟ أنت لست وحدك. ولكن الجميع بحاجة إلى معرفة ذلك. التطبيع يمكن أن يوفر لك الكثير من المشاكل. عاجلاً أم آجلاً ، يحدث شيء مشابه للشيء الموضح في الشكل التالي مع أي مطور.
Zoë ليس Zoëوهذا ، بالمناسبة ، ليس مثالًا لجافا سكريبت
غريبة أخرى. يقول مؤلف المادة ، التي نُنشر ترجمتها اليوم ، إنه يستطيع أن يظهر كيف تتجلى المشكلة نفسها عند استخدام كل لغة البرمجة الحالية تقريبًا. على وجه الخصوص ، نحن نتحدث عن البرامج النصية Python و Go ، وحتى shell. كيف تتعامل معها؟
قبل التاريخ
واجهت مشكلة Unicode لأول مرة منذ عدة سنوات عندما كتبت تطبيقًا (في Objective-C) قام باستيراد قائمة جهات اتصال من دفتر عناوين المستخدم ومن شبكاته الاجتماعية ، وبعد ذلك قمت بإزالة التكرارات. في بعض الحالات ، اتضح أن بعض الأشخاص مدرجون في القائمة مرتين. حدث هذا بسبب حقيقة أن أسمائهم ، وفقًا للبرنامج ، لم تكن نفس السلسلة.
على الرغم من أنه في المثال أعلاه ، يبدو السطران متماثلين تمامًا ، إلا أن الطريقة التي يتم بها عرضهما في النظام ، تختلف البايتات التي يتم تخزينهما بها على القرص. في الاسم الأول ،
"Zoë"
تمثل الحرف e (e مع umlaut) نقطة شفرة Unicode واحدة. في الحالة الثانية ، نحن نتعامل مع التحلل ، مع نهج تمثيل الشخصيات باستخدام العديد من الشخصيات. إذا كنت ، في التطبيق الخاص بك ، تعمل مع سلاسل Unicode ، فأنت بحاجة إلى التفكير في إمكانية تمثيل نفس الأحرف بطرق مختلفة.
كيف وصلنا إلى رموز تعبيرية: باختصار حول ترميز الأحرف
أجهزة الكمبيوتر تعمل مع بايت ، والتي هي مجرد أرقام. من أجل التمكن من معالجة النصوص على أجهزة الكمبيوتر ، وافق الناس على مراسلات الشخصيات والأرقام ، وتوصلوا إلى اتفاقات حول كيفية ظهور التمثيل البصري للشخصيات.
تم تمثيل أول اتفاقية من هذا القبيل من خلال ترميز ASCII (الكود القياسي الأمريكي لتبادل المعلومات). استخدم هذا الترميز 7 بتات ويمكن أن يمثل 128 حرفًا ، بما في ذلك الأبجدية اللاتينية (الأحرف الكبيرة والصغيرة) والأرقام وعلامات الترقيم الأساسية. تضمن ASCII أيضًا العديد من الأحرف "غير القابلة للطباعة" ، مثل موجز الأسطر وعلامات التبويب وإرجاع الأحرف وغيرها. على سبيل المثال ، في ASCII ، يتم ترميز الحرف اللاتيني M (الأحرف الكبيرة m) بالرقم 77 (4D بترميز سداسي عشري).
تتمثل مشكلة ASCII في أنه على الرغم من أن 128 حرفًا قد تكون كافية لتمثيل جميع الأحرف التي يستخدمها الأشخاص الذين يستخدمون نصوص باللغة الإنجليزية عادة ، فإن هذا العدد من الأحرف لا يكفي لتمثيل النصوص بلغات أخرى وشخصيات خاصة مختلفة مثل الرموز التعبيرية.
كان حل هذه المشكلة هو اعتماد معيار Unicode ، والذي كان يهدف إلى إمكانية تمثيل كل شخصية مستخدمة في جميع النصوص الحديثة والقديمة ، بما في ذلك شخصيات مثل الرموز التعبيرية. على سبيل المثال ، في أحدث إصدار من Unicode 12.0 ، يوجد أكثر من 137000 حرف.
يمكن تنفيذ معيار Unicode باستخدام مجموعة متنوعة من أساليب ترميز الأحرف. الأكثر شيوعا هي UTF-8 و UTF-16. تجدر الإشارة إلى أنه في الفضاء على شبكة الإنترنت الأكثر شيوعا هو المعيار لنصوص الترميز UTF-8.
يستخدم معيار UTF-8 1 إلى 4 بايت لتمثيل الأحرف. UTF-8 عبارة عن مجموعة فرعية من ASCII ، لذا فإن أول 128 حرفًا لها تتطابق مع الأحرف الممثلة في جدول الشفرات ASCII. يستخدم مقياس UTF-16 ، من ناحية أخرى ، 2 إلى 4 بايت لتمثيل حرف واحد.
لماذا هناك كلا المعايير؟ والحقيقة هي أن النصوص باللغات الغربية عادة ما تكون مشفرة بكفاءة أكبر باستخدام معيار UTF-8 (حيث يمكن تمثيل معظم الأحرف في هذه النصوص كرموز بحجم بايت واحد). إذا تحدثنا عن اللغات الشرقية ، فيمكننا القول أن الملفات التي تخزن النصوص المكتوبة بهذه اللغات عادةً ما تكون أقل عند استخدام UTF-16.
نقاط كود يونيكود وترميز الحروف
يتم تخصيص رقم تعريف لكل حرف في معيار Unicode يسمى نقطة الرمز. على سبيل المثال ، رمز نقطة رمز تعبيري

هو
U + 1F436 .
عند ترميز هذا الرمز ، يمكن تمثيله على هيئة تسلسلات مختلفة من البايتات:
- UTF-8: 4 بايت ،
0xF0 0x9F 0x90 0xB6
- UTF-16: 4 بايت ،
0xD83D 0xDC36
في شفرة JavaScript أدناه ، تطبع جميع الأوامر الثلاثة الحرف نفسه على وحدة تحكم المتصفح.
//
console.log('
') // => 
// Unicode (ES2015+)
console.log('\u{1F436}') // => 
// UTF-16
// ( 2 )
console.log('\uD83D\uDC36') // => 
تستخدم الآليات الداخلية لمعظم مترجمي JavaScript (بما في ذلك Node.js والمتصفحات الحديثة) UTF-16. هذا يعني أن أيقونة الكلاب التي ندرسها يتم تخزينها باستخدام وحدتي كود UTF-16 (16 بت لكل منهما). لذلك ، ما يجب أن لا يبدو مخرجات التعليمة البرمجية التالية غير مفهوم بالنسبة لك:
console.log('
'.length) // => 2
مزيج الشخصية
الآن عدنا إلى حيث بدأنا ، وبالتحديد ، دعنا نتحدث عن السبب في أن الرموز التي تبدو متشابهة بالنسبة للشخص لها تمثيل داخلي مختلف.
تم تصميم بعض أحرف Unicode لتعديل الأحرف الأخرى. يطلق عليهم الجمع بين الشخصيات. تنطبق على الأحرف الأساسية ، على سبيل المثال:
n + ˜ = ñ
u + ¨ = ü
e + ´ = é
كما ترون من المثال السابق ، تتيح لك الأحرف القابلة للدمج إضافة علامات التشكيل إلى الأحرف الأساسية. لكن قدرات تحويل أحرف Unicode لا تقتصر على ذلك. على سبيل المثال ، يمكن تمثيل بعض تسلسل الأحرف كحروف (حتى يمكن أن تتحول إلى æ).
المشكلة هي أنه يمكن تمثيل الشخصيات الخاصة بطرق مختلفة.
على سبيل المثال ، يمكن تمثيل الحرف é بطريقتين:
- باستخدام نقطة كود واحدة U + 00E9 .
- باستخدام مزيج من الحرف e والعلامة الحادة ، أي باستخدام نقطتي كود - U + 0065 و U + 0301 .
ستبدو الأحرف الناتجة عن استخدام أي من هذه الطرق لتمثيل الحرف é هي نفسها ، ولكن عند المقارنة ، يتبين أن الأحرف مختلفة. سيكون للخطوط التي تحتوي عليها أطوال مختلفة. يمكنك التحقق من ذلك عن طريق تشغيل الكود التالي في وحدة تحكم المتصفح.
console.log('\u00e9') // => é console.log('\u0065\u0301') // => é console.log('\u00e9' == '\u0065\u0301') // => false console.log('\u00e9'.length) // => 1 console.log('\u0065\u0301'.length) // => 2
هذا قد يؤدي إلى أخطاء غير متوقعة. على سبيل المثال ، يمكن التعبير عنها في حقيقة أن البرنامج ، لأسباب غير معروفة ، غير قادر على العثور على بعض الإدخالات في قاعدة البيانات ، بحيث لا يتمكن المستخدم ، بإدخال كلمة المرور الصحيحة ، من تسجيل الدخول إلى النظام.
تطبيع الخط
المشاكل المذكورة أعلاه لها حل بسيط ، والذي يتمثل في تطبيع الأوتار ، في جلبهم إلى "التمثيل القانوني".
هناك أربعة أشكال قياسية (الخوارزميات) للتطبيع:
- NFC: تطبيع التكوين الكنسي.
- NFD: تطبيع نموذج الكنسي التحلل.
- NFKC: تكوين توافق نموذج التطبيع.
- NFKD: تحليل توافق نموذج التطبيع.
الشكل الأكثر شيوعًا للتطبيع هو NFC. عند استخدام هذه الخوارزمية ، تتحلل جميع الأحرف أولاً ، وبعد ذلك يتم إعادة تكوين كل متواليات الدمج بالترتيب المحدد بواسطة المعيار. للاستخدام العملي ، يمكنك اختيار أي شكل. الشيء الرئيسي هو تطبيقه باستمرار. نتيجة لذلك ، سيؤدي استلام نفس البيانات عند إدخال البرنامج دائمًا إلى نفس النتيجة.
في JavaScript ، بدءًا بمعيار ES2015 (ES6) ، هناك طريقة مضمّنة لتطبيع السلاسل -
String.prototype.normalize ([form]) . يمكنك استخدامه في بيئة Node.js وفي جميع المتصفحات الحديثة تقريبًا. وسيطة
form
في هذه الطريقة هي معرف السلسلة لنموذج التطبيع. الافتراضي هو نموذج NFC.
نعود إلى المثال السابق النظر ، مع تطبيق التطبيع هذه المرة:
const str = '\u0065\u0301' console.log(str == '\u00e9') // => false const normalized = str.normalize('NFC') console.log(normalized == '\u00e9') // => true console.log(normalized.length) // => 1
النتائج
إذا كنت تقوم بتطوير تطبيق ويب وتستخدم ما يدخله المستخدم ، فعليك دائمًا تطبيع البيانات النصية المستلمة. في JavaScript ، يمكنك استخدام طريقة السلسلة القياسية normal
تطبيع () لتنفيذ التطبيع.
أعزائي القراء! هل واجهت مشاكل مع السلاسل التي يمكن حلها بالتطبيع؟
