المستوى: مبتدئ — المقال ده موجّه للمبتدئين. لو سبق وفتحت ملف أو رسالة أو صفحة ولقيت اسمك العربي اتحوّل لرموز غريبة، هتعرف هنا ليه بالظبط، وتقدر تصلّحها في دقيقة.
ليه اسمك بالعربي بيظهر «Ø£Ù‡Ù„ا»؟ الحكاية اسمها الترميز
الكمبيوتر مبيعرفش حروف. بيعرف أرقام بس. أي حرف بتكتبه بيتحوّل لرقم، والرقم بيتخزّن كبايتات. لو الجهاز اللي كتب النص والجهاز اللي قراه اتفقوا على نفس «جدول الأرقام»، الكلام يظهر صح. لو اختلفوا، تطلع الرموز الغريبة دي. المشكلة مش في اللغة العربية، ولا في جهازك. المشكلة في اختلاف الترميز بين الكتابة والقراءة.
المشكلة باختصار
عندك سطر مكتوب صح: «أهلاً». تبعته لزميلك أو تصدّره في ملف، يوصله «Ø£Ù‡Ù„اً». الحروف اتبدّلت بحاجة ملهاش معنى. الاسم العلمي للظاهرة دي هو mojibake. وبتحصل لأن النص اتكتب بترميز، واتقرا بترميز تاني. وده مهم لأي حد بيشتغل بالعربي: أسماء، عناوين، رسائل، ملفات CSV، قواعد بيانات. غلطة ترميز واحدة تفسد آلاف الصفوف.
الفكرة الأول بمثال: الشفرة السرية بين صاحبين
تخيّل إنت وصاحبك عملتوا شفرة: كل حرف ليه رقم. A=1، B=2، وهكذا. لو بعتّله «2 1 20»، هو يفكّها لـ «B A T». تمام، لأنكم الاتنين ماسكين نفس الورقة.
دلوقتي تخيّل صاحبك مسك ورقة تانية، فيها A=100 وB=200. هيقرا نفس الأرقام ويطلعله كلام مختلف تمامًا. مفيش حد غلط في العملية الحسابية، بس الجدول اختلف، فالنتيجة بقت هباب. الكمبيوتر بيعمل نفس الحكاية بالظبط: «الورقة» دي اسمها الترميز (encoding).
الشرح العلمي: من الحرف للبايت
خلينا نفكّها بدقة على تلات مستويات:
- نقطة الكود (code point): معيار Unicode أعطى لكل حرف في كل لغات الدنيا رقمًا فريدًا. الحرف A رقمه U+0041، والحرف س رقمه U+0633. ده «الهوية» الثابتة للحرف.
- الترميز (encoding): إزاي نخزّن الرقم ده كبايتات فعليًا. UTF-8 هو الطريقة الأشهر. بيستخدم بايت واحد للحروف اللاتينية (نفس ASCII القديم)، وبايتين لأغلب الحروف العربية، وحتى 4 بايتات للإيموجي.
- البايتات: الأرقام اللي بتتخزّن فعلًا على القرص أو بتتبعت على الشبكة.
فالحرف A في UTF-8 بايت واحد قيمته 65 (أو 0x41). والحرف س بايتين قيمتهم 0xD8 و0xB3. القارئ لازم يعرف إن دول UTF-8 عشان يرجّعهم حروف صح.
جرّبها بنفسك: كود بايثون يوريك المشكلة والحل
انسخ الكود ده وشغّله. هتشوف الرموز الغريبة بتتولد قدامك، وبعدين تتصلّح:
text = "أهلاً"
# 1) الترميز الصح: نحوّل الحروف لبايتات UTF-8
data = text.encode("utf-8")
print(data) # b'\xd8\xa3\xd9\x87\xd9\x84\xd8\xa7\xd9\x8b'
print(len(text), "رمز /", len(data), "بايت") # 5 رمز / 10 بايت
# 2) القراءة الغلط: نفتح نفس البايتات كأنها Windows-1252
print(data.decode("cp1252")) # أهلاً <-- الرموز الغريبة
# 3) الحل: اقرأ بنفس ترميز الكتابة
print(data.decode("utf-8")) # أهلاً