ملخص
يحفظ std::string بايتات لا حروفًا، وفي نصوص اليوم قد يشغل الحرف الواحد من بايت واحد إلى أربعة بايتات. يفصل هذا الدرس بين ثلاثة أشياء يخلط بينها الكلام اليومي: المحرف المجرد، الذي يعرّفه يونيكود برقم يسمى نقطة الترميز؛ والبايتات التي يرمّزه بها UTF-8؛ والشكل الذي ترسمه الشاشة. ويشرح كيف يوزّع UTF-8 بتات نقطة الترميز على بايتاتها، ولماذا تشغل كلمة «باب» ستة بايتات، وكيف تُعدّ محارف سلسلة نصية دون فكّ ترميزها. ثم يبيّن أي العمليات تُفسد النص (العكس بايتًا بايتًا، والتحويل إلى الأحرف الكبيرة بايتًا بايتًا، والقطع في وسط محرف)، وأيها تعمل دون أي احتياط (الحفظ والنسخ والضم والبحث والكتابة)، وما الذي لا تفعله المكتبة القياسية في C++23: الأحرف الكبيرة خارج ASCII، والترتيب الأبجدي بحسب اللغة، والتطبيع.
أهداف التعلم
- أن يشرح المتعلم كيف يرمّز UTF-8 نقطة ترميز في بايت واحد إلى أربعة بايتات، وأن يفكّ يدويًا ترميز بايتات الحرف الإسباني «ñ».
- أن يتنبأ بحجم سلسلة نصية بالبايتات إذا احتوت حروفًا من خارج ASCII، مثل
"باب"(6) أو"البرمجة"(14). - أن يعدّ محارف سلسلة نصية بترميز UTF-8 بعدّ البايتات التي ليست بايتات استمرار، بالشرط
(c & 0xC0) != 0x80. - أن يتعرّف العمليات التي تُفسد نص UTF-8 (العكس بايتًا بايتًا، و
std::toupperلكل بايت، والقطع في وسط محرف)، وأن يقرّر ما يفعله في كل حالة.
المحارف ونقاط الترميز والبايتات
ثلاثة مستويات
قدّم الدرس المحارف والقيم المنطقية والتحويلات والثوابت النوع char عددًا صحيحًا من بايت واحد يُكتب حرفًا بحسب جدول ASCII، ونبّه إلى أن 'ر' لا يتّسع له؛ وبيّن الدرس الترجمة والتنفيذ والإعداد أن "سنة" تشغل ستة بايتات، وهيّأ الطرفية لكتابتها. وهذا الدرس يشرح السبب. يُسند ASCII رقمًا إلى 128 محرفًا، من 0 إلى 127، تتّسع لها سبعة بتات: حروف الإنجليزية والأرقام وعلامات الترقيم وبعض رموز التحكم. غير أن النص الحقيقي يحتوي حروفًا عربية وحروفًا لاتينية ذات علامات وحروفًا يونانية ورموزًا صينية ورموزًا رياضية ورموزًا تعبيرية، يبلغ مجموعها مئات الآلاف. فيلزم من ذلك أمران: أولًا جدول أكبر بكثير، وثانيًا طريقة لكتابة أرقامه بالبايتات.
يحسن التمييز بين ثلاثة مستويات. الأول هو المحرف المجرد: الحرف الإسباني «ñ الصغير»، بصرف النظر عن طريقة حفظه أو رسمه. ويونيكود هو المعيار الذي يُسند إلى كل محرف مجرد رقمًا، هو نقطة الترميز الخاصة به، ويُكتب U+ متبوعًا بأربعة أرقام ست عشرية على الأقل: فالحرف «ñ» هو U+00F1 (241)، والحرف «A» هو U+0041 (65، وهو الرقم نفسه في ASCII)، والرمز «€» هو U+20AC (8364). وتمتد نقاط الترميز من 0 إلى U+10FFFF، أي أكثر من مليون بقليل. والمستوى الثاني هو الترميز: القاعدة التي تحوّل كل نقطة ترميز إلى متتالية من البايتات. وUTF-8 هو الترميز الذي تستعمله الشبكة والأنظمة الحالية وهذه الدورة. والمستوى الثالث هو الإظهار: الشكل الذي يرسمه الخط على الشاشة، وقد يتغيّر من خط إلى آخر دون أن يتغيّر المحرف.
وإذا كان الأمر كذلك، فالمعطى النصي ليس نصًا ما دام ترميزه غير معروف: البايتان c3 b1 هما الحرف «ñ» في UTF-8، لكنهما إذا قُرئا بجدول طرفية قديمة كانا رمزين لا علاقة لهما به، وهو العطل الذي علّم الدرس 1.4 تشخيصه. والمسألة نفسها، من جهة تنضيد الوثائق، تظهر في درس «اللغات والخطوط» من دورة LaTeX للكتابة الأكاديمية.
رؤية البايتات
لرؤية بايتات سلسلة نصية يكفي اجتيازها: فكل من std::string وstd::string_view متتالية من char، وحلقة for على النطاق تُسلّم بايتاتها واحدًا واحدًا. ولكتابتها بالنظام الست عشري يصلح الحقل {:02x} (رقمان ست عشريان، مع صفر على اليسار). وكما في {:d} في الدرس 2.4، وبسبب التصحيح نفسه للمعيار الذي اعتُمد في 2023 وطبّقه GCC 15، يكتب {:02x} قيمة char بايتًا بلا إشارة. ولهذه القاعدة أهمية، لأن char ذو إشارة في GCC على x86-64: فالبايت c3، إذا حُوّل إلى int، كان العدد ، الذي كان {:02x} سيكتبه -3d.
#include <print>#include <string_view>// بايتات نص.// الشرط المسبق: لا شيء.// الشرط اللاحق: يكتب سطرًا فيه text وحجمه بالبايتات وكل بايت بالنظام الست عشري.void printBytes(std::string_view text){ std::print("{:<4}{}:", text, text.size()); for (const char c : text) { std::print(" {:02x}", c); } std::println("");}int main(){ printBytes("A"); printBytes("ñ"); printBytes("€"); printBytes("😀"); printBytes("باب");}A 1: 41ñ 2: c3 b1€ 3: e2 82 ac😀 4: f0 9f 98 80باب 6: d8 a8 d8 a7 d8 a8يُظهر كل سطر النص وحجمه وبايتاته. محرف من كل طول: الحرف «A» يشغل بايتًا واحدًا، والحرف «ñ» اثنين، والرمز «€» ثلاثة، والرمز التعبيري أربعة. وكلمة «باب» تشغل ستة بايتات، d8 a8 وd8 a7 وd8 a8، وقيمة size() لها 6 لا 3. ويُلاحظ أن الحرف «A» هو البايت 41، وهو نفسه في ASCII: فـUTF-8 يكتب محارف ASCII الـ128 كما يكتبها ASCII تمامًا.
يتتبّع الشكل ترميز الحرف «ñ» من الأعلى إلى الأسفل: نقطة الترميز، وبتاتها، وتوزيع هذه البتات، والبايتين الناتجين، مع إبراز بتات البادئة.
البرهان
الحرف «ñ» هو نقطة الترميز
U+00F1، أي .بالنظام الثنائي، يُكتب على الصورة
11110001. وهو أكبر من 127 وأصغر من 2048، فيكتبه UTF-8 في بايتين ينقلان أحد عشر بتًا: يُكمَّل بأصفار على اليسار حتى أحد عشر،00011110001.تتوزّع البتات الأحد عشر على مجموعتين: الخمسة الأولى،
00011، والستة الأخيرة،110001.يحمل البايت الأول البادئة
110، التي تُعلن محرفًا من بايتين، وتليها البتات الخمسة للمجموعة الأولى:11000011. ويحمل الثاني البادئة10، التي تَسِم بايت استمرار، وتليها البتات الستة الباقية:10110001.بالنظام الست عشري،
1100 0011هوc3و1011 0001هوb1: البايتان اللذان كتبهما البرنامج المتقدّم للحرف «ñ». وتغيير الأساس بين الثنائي والست عشري، أربعة بتات أربعة بتات، هو موضوع درس «أنظمة العدّ الموضعية» من دورة الجبر والهندسة 1.
جارٍ تحميل المحتوى…