تهدف هذه المقالة إلى تجميع وتفكيك مبادئ وآلية عمل ترميزات النص ، بالتفصيل هذه الآلية لتفكيكها وشرحها. سيكون مفيدًا لأولئك الذين يتخيلون تقريبًا فقط ترميزات النص وكيف تعمل ، وكيف تختلف عن بعضها البعض ، ولماذا تظهر أحيانًا أحرف غير قابلة للقراءة ، وما هو مبدأ الترميز الذي تملكه الترميزات المختلفة.
للحصول على فهم مفصل لهذه المشكلة ، يجب عليك قراءة وتجميع أكثر من مقالة واحدة وقضاء الكثير من الوقت في هذا الشأن. في هذه المادة ، يتم وضع كل ذلك معًا ، ومن الناحية النظرية ، يجب توفير الوقت والتحليل في رأيي كان مفصلاً تمامًا.
ماذا سيحدث في إطار عملية القطع: مبدأ تشغيل ترميزات البايت الواحد (ASCII ، Windows-1251 ، وما إلى ذلك) ، المتطلبات المسبقة لظهور Unicode ، ما هو Unicode ، ترميزات Unicode UTF-8 ، UTF-16 ، اختلافاتهم ، ميزاتهم الأساسية ، التوافق وعدم توافق الترميزات المختلفة ، مبادئ ترميز الأحرف ، التحليل العملي للتشفير وفك التشفير.
من المؤكد أن مشكلة الترميزات قد فقدت أهميتها ، لكنني أعتقد أنه لن يكون من الضروري معرفة كيف تعمل الآن وكيف كانت تعمل من قبل.
متطلبات Unicode
أعتقد أن الأمر يستحق أن يبدأ من الوقت الذي لم يتم فيه تطوير الحوسبة بعد وكان يكتسب زخماً فقط. ثم لم يظن المطورون والمقيسون أن أجهزة الكمبيوتر والإنترنت ستكتسب هذه الشعبية الضخمة وانتشارها. في الواقع ثم نشأت الحاجة لتشفير النص. في أي شكل كان من الضروري تخزين الحروف في الكمبيوتر ، وهو (الكمبيوتر) يفهم فقط الأصفار والأصفار. لذلك تم تطوير ترميز ASCII أحادي البايت (على الأرجح أنه ليس الترميز الأول ، ولكنه الأكثر شيوعًا وإرشاديًا ، لذلك سننظر فيه كمرجع). ما هي تحب؟ يتم ترميز كل حرف في هذا الترميز بثمانية بتات. من السهل حساب أنه ، بناءً على ذلك ، يمكن أن يحتوي الترميز على 256 حرفًا (ثمانية بتات ، أصفار أو تلك 2
8 = 256).
أول 7 بت (128 حرفًا 2
7 = 128) في هذا الترميز تم منحها للأحرف اللاتينية وأحرف التحكم (مثل فواصل الأسطر وعلامات التبويب وما إلى ذلك) والأحرف النحوية. تم حجز الباقي للغات الوطنية. بمعنى أنه تبين أن أول 128 حرفًا هي نفسها دائمًا ، وإذا كنت تريد ترميز لغتك الأم ، فيرجى استخدام السعة المتبقية. في الواقع ، ظهرت حديقة حيوان ضخمة من الترميزات الوطنية. والآن يمكنك أن تتخيل نفسك ، على سبيل المثال ، عندما أكون في روسيا ، أقوم بإنشاء مستند نصي وإنشاءه بشكل افتراضي ، يتم إنشاؤه في ترميز Windows-1251 (الترميز الروسي المستخدم في Windows) وإرساله إلى شخص ما ، على سبيل المثال ، في الولايات المتحدة الأمريكية. حتى حقيقة أن المحاور الخاص بي يعرف أن الروسية لن تساعده ، لأنه عندما يفتح المستند الخاص بي على جهاز الكمبيوتر الخاص به (في المحرر مع الترميز الافتراضي لنفس ASCII) ، فلن يرى الرسائل الروسية ، ولكن krakozyabry. لكي نكون أكثر دقة ، سيتم عرض تلك الأماكن في الوثيقة التي أكتبها باللغة الإنجليزية دون مشاكل ، لأن أول 128 حرفًا من ترميزات Windows-1251 و ASCII هي نفسها ، لكن حيث كتبت النص الروسي ، إذا لم يشير إلى الترميز الصحيح في محرره ، في شكل تمساح.
أعتقد أن مشكلة الترميزات الوطنية أمر مفهوم. في الواقع ، هناك الكثير من هذه الترميزات الوطنية ، وأصبح الإنترنت واسعًا للغاية ، والجميع في ذلك يرغب في الكتابة بلغتهم الخاصة ولا يريدون أن تبدو لغتهم مثل الشعر الملتوي. كانت هناك طريقتان ، للإشارة لكل صفحة من الترميز ، أو لإنشاء جدول واحد من الأحرف المشتركة بين جميع الأحرف في العالم. فاز الخيار الثاني ، لذلك أنشأنا جدول أحرف Unicode.
ورشة عمل ASCII الصغيرة
قد يبدو الأمر أساسيًا ، لكن بما أنني قررت شرح كل شيء بالتفصيل ، فهذا ضروري.
فيما يلي جدول أحرف ASCII:

هنا لدينا 3 أعمدة:
- رقم الحرف العشري
- رقم الحرف بالتنسيق الست عشري
- تمثيل الرمز نفسه.
لذلك ، قم بترميز السلسلة "ok" (ASCII). يحتوي الحرف "o" (eng.) على 111 في عشري و
6 F في ست عشري.
01101111
نترجم هذا إلى نظام ثنائي -
01101111
. الرمز "k" (eng.) - الموضع 107 في رقم عشري و
6 B في ست عشري ، يترجم إلى ثنائي -
01101011
. ستبدو السلسلة "OK" المشفرة في ASCII بالشكل التالي -
01101111 01101011
. ستكون عملية فك الترميز عكسية. نأخذ 8 بتات ، ونترجمها إلى ترميز عشري 10 ، ونحصل على رقم الحرف ، وننظر إلى الجدول إلى أي نوع من الأحرف هو.
يونيكود
مع الشروط الأساسية لإنشاء جدول مشترك للجميع في عالم الشخصيات ، مرتبة. الآن ، في الواقع ، إلى الطاولة نفسها. Unicode - هذا هو الجدول الذي (هذا ليس ترميزًا ، لكنه جدول رموز). وهو يتألف من 111412 وظيفة. لم يتم شغل معظم هذه المواقف بعد برموز ، لذلك من غير المرجح أن يتم توسيع هذه المساحة.
تنقسم هذه المساحة الإجمالية إلى 17 قطعة ، 65536 حرفًا لكل منها. كل كتلة تحتوي على مجموعة من الشخصيات الخاصة بها. الكتلة الصفرية هي الكتلة الأساسية ، وهي تحتوي على أكثر الأحرف استخدامًا لكل الحروف الهجائية الحديثة. في الكتلة الثانية هي أحرف اللغات المنقرضة. هناك نوعان من القطع المخصصة للاستخدام الخاص. لم يتم شغل معظم الكتل بعد.
السعة الإجمالية لأحرف Unicode هي من
0 إلى
10FFFF (
بالسداسي عشري).
تتم كتابة الأحرف السداسية العشرية بالبادئة "U +". على سبيل المثال ، تتضمن الكتلة الأساسية الأولى أحرفًا من U + 0000 إلى U + FFFF (من 0 إلى 65.535) ، والكتلة السابعة عشرة الأخيرة من U + 100،000 إلى U + 10FFFF (من 1،048،576 إلى 1،114،111).
حسنًا الآن ، بدلاً من حديقة الترميزات الوطنية ، لدينا جدول شامل يتم فيه تشفير جميع الأحرف التي قد تكون مفيدة لنا. ولكن هناك أيضا عيوب. إذا تم تشفير كل حرف قبل بايت واحد ، يمكن الآن تشفيره بعدد مختلف من البايتات. على سبيل المثال ، لتشفير جميع أحرف الأبجدية الإنجليزية ، لا تزال بايت واحد كافية ، على سبيل المثال ، نفس الحرف "o" في اللغة الإنجليزية هو يونيكود U + 006F ، أي أن الرقم نفسه كما في ASCII هو
6F في الست عشري و 111 بالتدوين العشري. ولكن لتشفير حرف "
U + 103D5 " (هذا هو الرقم الفارسي القديم مائة) - 103D5 في الست عشري و 66،517 بالترتيب العشري ، هنا نحتاج إلى ثلاث بايت.
يجب أن تعمل ترميزات Unicode مثل UTF-8 و UTF-16 على حل هذه المشكلة بالفعل. كذلك سنتحدث عنها.
UTF-8
UTF-8 هو ترميز Unicode بطول متغير يمكن استخدامه لتمثيل أي حرف Unicode.
دعنا نتحدث أكثر عن الطول المتغير ، ماذا يعني؟ أول ما أقوله هو أن الوحدة الهيكلية (الذرية) لهذا الترميز هي بايت. حقيقة أن تشفير المتغير طويل يعني أنه يمكن تشفير حرف واحد مع عدد مختلف من الوحدات الهيكلية للتشفير ، أي بعدد مختلف من البايتات. على سبيل المثال ، يتم ترميز اللاتينية في بايت واحد ، والسيريلية في وحدتي بايت.
استطرادا قليلا من الموضوع ، فمن الضروري أن تكتب عن توافق ASCII و UTF
حقيقة أن الحروف اللاتينية وهياكل التحكم الأساسية ، مثل فواصل الأسطر وعلامات التبويب ، إلخ. ترميز مع بايت واحد يجعل ترميزات utf متوافقة مع ترميزات ASCII. وهذا هو ، في الواقع ، توجد الهياكل اللاتينية والتحكم في نفس الأماكن في كل من ASCII و UTF ، وحقيقة أنها مشفرة هناك وهناك مع بايت واحد يضمن هذا التوافق.
لنأخذ الحرف "o" من مثال ASCII أعلاه. تذكر أنه في جدول أحرف ASCII يكون في 111 موضعًا ، في شكل بت سيكون
01101111
. في جدول Unicode ، هذه الشخصية هي U + 006F ، والتي ستكون أيضًا
01101111
في شكل بت. والآن ، نظرًا لأن UTF عبارة عن ترميز بطول متغير ، سيتم تشفير هذه الشخصية في بايت واحد فيها. وهذا يعني أن تمثيل هذا الرمز في كلا الترميزين سيكون هو نفسه. وهكذا بالنسبة لمجموعة كاملة من الأحرف من 0 إلى 128. أي إذا كان المستند يتكون من نص باللغة الإنجليزية ، فلن تلاحظ الفرق إذا فتحته في تشفير UTF-8 و UTF-16 و ASCII (على سبيل المثال ، في UTF-16 هذه الأحرف كلها بالتساوي سيتم تشفيرها في وحدتي بايت ، لذلك لن ترى الفرق إذا كان المحرر يتجاهل صفر بايت) ، وهكذا حتى تبدأ العمل مع الأبجدية الوطنية.
دعنا نقارن في الممارسة العملية كيف ستبدو عبارة "Hello World" في ثلاثة ترميزات مختلفة: Windows-1251 (ترميز روسي) ، ISO-8859-1 (ترميز لغات أوروبا الغربية) ، UTF-8 (ترميز يونيكود). جوهر هذا المثال هو أن العبارة مكتوبة بلغتين. دعونا نرى كيف سيبدو في ترميزات مختلفة.
في الترميز ISO-8859-1 لا توجد مثل هذه الأحرف "m" و "و" و "p".الآن ، دعونا نعمل مع الترميزات ونرى كيفية تحويل سلسلة من ترميز إلى آخر وما سيحدث إذا كان التحويل غير صحيح ، أو لا يمكن القيام به بسبب الاختلاف في الترميزات.
نفترض أن العبارة تم ترميزها في الأصل في Windows-1251. استنادًا إلى الجدول أعلاه ، نكتب هذه العبارة في شكل ثنائي ، تم ترميزها في Windows-1251. للقيام بذلك ، نحتاج فقط إلى ترجمة الرموز من ثنائي إلى عشري أو ست عشري (من الجدول أعلاه).
01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000
حسنًا ، هذه هي عبارة "Hello World" المشفرة في Windows-1251.الآن تخيل أن لديك ملفًا به نص ، لكنك لا تعرف نوع ترميز هذا النص. تفترض أنه تم ترميزه في ISO-8859-1 وفتحه في برنامج التحرير الخاص بك في هذا الترميز. كما هو مذكور أعلاه ، مع وجود جزء من الرموز ، كل شيء في حالة جيدة ، فهي في هذا الترميز ، وحتى في نفس الأماكن ، ولكن مع رموز من كلمة "العالم" كل شيء أكثر تعقيدًا. هذه الأحرف ليست في هذا الترميز ، وفي مكانها في الترميز ISO-8859-1 أحرف مختلفة تمامًا. على وجه التحديد ، "m" هو الموضع 236 ، "و" هو 232. "p" هي 240. وفي هذه المواضع في تشفير ISO-8859-1 ، توجد المواضع التالية للأحرف 236 - الحرف "ì" ، 232 - "è" ، 240 - "ð"
لذا فإن عبارة "Hello World" المشفرة في Windows-1251 والمفتوحة في تشفير ISO-8859-1 ستبدو كما يلي: "Hello ìèð". لذلك اتضح أن هذين الترميزين متوافقان جزئيًا فقط ، ولن يعمل بشكل صحيح لتشفير سلسلة من ترميز إلى آخر ، لأنه ببساطة لا توجد مثل هذه الأحرف.
هنا ستكون هناك حاجة إلى ترميزات Unicode ، وتحديداً في هذه الحالة ، ضع في الاعتبار UTF-8. حقيقة أنه يمكن ترميز الأحرف الموجودة به بعدد مختلف من البايتات من 1 إلى 4 اكتشفناها بالفعل. تجدر الإشارة الآن إلى أن استخدام UTF يمكن تشفيره ليس فقط 256 حرفًا ، كما في الحرفين السابقين ، ولكن فقط تفعل كل أحرف Unicode
وهي تعمل على النحو التالي. البتة الأولى من كل بايت من حرف الترميز ليست مسؤولة عن الحرف نفسه ، ولكن عن تحديد البايت. هذا ، على سبيل المثال ، إذا كانت البادئة (الأولى) تساوي صفرًا ، فهذا يعني أن بايت واحد فقط يتم استخدامه لتشفير حرف. الذي يوفر التوافق مع ASCII. إذا نظرت بعناية إلى جدول أحرف ASCII ، فسترى أن أول 128 حرفًا (الأبجدية الإنجليزية وشخصيات التحكم وعلامات الترقيم) إذا تم تحويلها إلى ثنائي ، فكل شيء يبدأ بـ صفر بت (كن حذرًا إذا قمت بترجمة الأحرف إلى نظام ثنائي باستخدام على سبيل المثال عبر الإنترنت المحول ، ثم يمكن تجاهل أول بت صفر الرائدة ، والتي يمكن أن تكون مربكة).
01001000
-
01001000
الأولى
01001000
صفرًا ، ثم البايت
01001000
يشفر حرفًا واحدًا -> "H"
01100101
- البتة الأولى هي صفر ، مما يعني أن 1 بايت يشفر حرفًا واحدًا -> "e"
إذا لم تكن البتة الأولى صفراً ، فسيتم تشفير الحرف بعدة بايت.
بالنسبة إلى الأحرف مزدوجة البايت ، يجب أن تكون البتات الثلاثة الأولى - 110
110 10000 10 111100
- في بداية 110 ، ثم 2 بايت ترميز حرف واحد. دائمًا ما تبدأ البايتة الثانية في هذه الحالة ب 10. إجمالًا ، تجاهل بتات التحكم (الأولى منها ، المظللة باللون الأحمر والأخضر)
10000111100
كل
10000111100
المتبقية (
10000111100
) ،
10000111100
بترجمتها إلى ست عشري (043C) -> U + 043C في Unicode ، الرمز "m ".
للأحرف ثلاثية البايت في البايت الأول ، البتات البادئة هي 1110
1110 1000 10 000111 10 1010101
- نلخص كل شيء ما عدا وحدات التحكم ونحصل على أنه في الست عشري هو 103V5 ، U + 103D5 هو الرقم الفارسي القديم مائة (
10000001111010101
)
للأحرف الأربعة بايت في البايت الأول ، البتات البادئة هي 11110
11110 100 10 001111 10 111111 10 111111
- U + 10FFFF هي آخر شخصية صالحة في جدول يونيكود (
100001111111111111111
)
الآن ، إذا رغبت في ذلك ، يمكننا تسجيل العبارة الخاصة بنا بترميز UTF-8.
UTF-16
UTF-16 هو أيضًا ترميز بطول متغير. الفارق الرئيسي بينه وبين UTF-8 هو أن الوحدة الهيكلية فيه ليست بايت واحد. وهذا هو ، في ترميز UTF-16 ، يمكن ترميز أي حرف Unicode مع اثنين أو أربعة بايت. من أجل الوضوح ، واسمحوا لي أن أسمي زوج من هذه البايتات زوج الرموز. بناءً على ذلك ، يمكن تشفير أي حرف Unicode تم ترميزه في UTF-16 إما باستخدام زوج واحد من الرموز أو اثنين.
لنبدأ بالأحرف التي تم ترميزها بواسطة زوج واحد من الكود. من السهل حساب أنه يمكن أن يكون هناك 65،535 مثل هذه الأحرف (2v16) ، والتي تتزامن تمامًا مع كتلة Unicode الأساسية. سيتم تشفير جميع الأحرف الموجودة في كتلة Unicode بترميز UTF-16 مع زوج واحد من الكود (وحدتي بايت) ، كل شيء بسيط هنا.
الرمز "o" (لاتيني) -
00000000 01101111
الرمز "M" (السيريلية) -
00000100 00011100
الآن النظر في الأحرف خارج نطاق Unicode الأساسي. من أجل الترميز الخاص بهم ، يلزم وجود أزواج من الشفرات (4 بايت). وآلية ترميزها أكثر تعقيدًا قليلاً ، دعنا نذهب بالترتيب.
بادئ ذي بدء ، نقدم مفاهيم زوج بديل. الزوج البديل هو زوجان من الرموز يستخدمان لتشفير حرف واحد (إجمالي 4 بايت). لمثل هذه الأزواج البديلة ،
يتم تعيين نطاق خاص من
D800 إلى
DFFF في جدول Unicode. هذا يعني أنه عند تحويل زوج من الكود من نموذج بايت إلى ست عشري ، ستحصل على رقم من هذا النطاق ، فهذا ليس حرفًا مستقلًا ، بل هو زوج بديل.
لتشفير حرف من النطاق
10000 -
10FFFF (أي ، حرف تحتاج إلى استخدام أكثر من زوج رمز) ، فأنت تحتاج إلى:
- اطرح 10000 (ست عشري) من رمز الحرف (هذا هو أصغر رقم من النطاق 10000 - 10FFFF )
- نتيجةً للنقطة الأولى ، سيتم الحصول على رقم لا يزيد عن FFFFF ، ويشغل حتى 20 بت
- يتم تلخيص البتات العشرة الأولى من العدد المستلم بـ D800 (بداية مجموعة الأزواج البديلة في Unicode)
- يتم تلخيص الـ 10 بتات التالية باستخدام DC00 (أيضًا رقم من مجموعة الأزواج البديلة)
- بعد ذلك نحصل على زوجان بديلان لكل منهما 16 بت ، أول 6 بتات في كل زوج من هذا القبيل مسؤولة عن تحديد أنه بديل ،
- البتة العاشرة في كل بديل هي المسؤولة عن ترتيبها ؛ إذا كانت 1 ، فهذا هو البديل الأول ، إذا كان 0 ، ثم الثاني
سنقوم بتحليل هذا في الممارسة العملية ، وأعتقد أنه سوف يصبح أكثر وضوحا.
على سبيل المثال ، نقوم بتشفير الرمز ، ثم فك تشفيره. خذ الرقم الفارسي القديم مائة (U + 103D5):
- 103D5 - 10000 = 3D5
0000000000 1111010101
= 0000000000 1111010101
(تحولت البتات العشرة الأولى إلى صفر ، نصل هذا إلى رقم سداسي عشري ، نحصل على 0 (العشرة الأولى) ، 3D5 (العشرة الثانية))- 0 + D800 = D800 (
110110 0 000000000
) تحدد أول 6 بتات أن الرقم من مجموعة الأزواج البديلة للبت العاشرة (على اليمين) يساوي صفرًا ، ثم هذا هو البديل الأول - 3D5 + DC00 = DFD5 (
110111 1 111010101
) تحدد أول 6 بتات أن الرقم الموجود في نطاق الأزواج البديلة هو البتة العاشرة (على اليمين) واحد ، ثم هذا هو البديل الثاني - مجموع هذه الشخصية في UTF-16 هو
1101100000000000 1101111111010101
الآن فك شفرة العكس. دعنا نقول أن لدينا مثل هذا الرمز - 1101100000100010 1101111010001000:
- ترجم إلى شكل سداسي عشري = D822 DE88 (كلتا القيمتين من نطاق أزواج بديلة ، لذلك أمامنا زوج بديل)
110110 0 000100010
- 110110 0 000100010
العاشرة (على اليمين) 110110 0 000100010
صفراً ، ثم البديل الأول110111 1 010001000
- البت العاشر (على اليمين) واحد ، ثم البديل الثاني- نحن نتجاهل 6 بت من المسؤولين عن تحديد البديل ، ونحصل على
0000100010 1010001000
( 8A88 ) - أضف 10،000 (نطاقات بديلة أقل) 8A88 + 10000 = 18A88
- ابحث في جدول يونيكود عن الحرف U + 18A88 = Tangut Component-649. مكونات البرنامج النصي Tangut.
بفضل أولئك الذين تمكنوا من القراءة حتى النهاية ، آمل أن يكون مفيدًا وليس مملًا جدًا.
فيما يلي بعض الروابط المثيرة للاهتمام حول هذا الموضوع:
habr.com/en/post/158895 - معلومات عامة مفيدة عن الترميزات
habr.com/en/post/312642 - حول يونيكود
unicode-table.com/ru - جدول أحرف Unicode نفسه
حسنًا ، في الواقع أين ستكون بدونها
en.wikipedia.org/wiki/٪D0٪AE٪D0٪BD٪D0٪B8٪D0٪BA٪D0٪BE٪D0٪B4 - Unicode
en.wikipedia.org/wiki/ASCII - ASCII
en.wikipedia.org/wiki/UTF-8 - UTF-8
en.wikipedia.org/wiki/UTF-16 - UTF-16