المحارف غير المرئية في Unicode لا تشغل أي مساحة مرئية لكنها تبقى جزءًا من السلسلة النصية. وأشهرها المسافات الصفرية والواصلات الناعمة وعلامات ترتيب البايتات والمسافات غير الفاصلة وعلامات الاتجاه. لكل منها غرض طباعي مشروع، لكن بقاءها بعد النسخ واللصق يعطّل البحث والتحقق والتحليل وإزالة التكرارات.
المحارف التي ستصادفها فعلًا
| المحرف | نقطة الترميز | الغرض | ما الذي يعطّله |
|---|---|---|---|
| المسافة الصفرية | U+200B | اقتراح موضع لكسر السطر | البحث ومقارنة السلاسل |
| المانع الصفري للاتصال | U+200C | يمنع اتصال الحروف | الحروف المتراكبة والنص العربي والهندي |
| الواصل الصفري | U+200D | يفرض اتصال الحروف | تسلسلات الرموز التعبيرية وطول النص |
| الواصلة الناعمة | U+00AD | واصلة تظهر عند الالتفاف فقط | البحث والنص المنسوخ |
| المسافة غير الفاصلة | U+00A0 | مسافة لا تلتف أبدًا | القص وتحليل CSV والشيفرة |
| المسافة الضيقة غير الفاصلة | U+202F | مسافة رفيعة قبل الترقيم | مثل ما سبق |
| علامة ترتيب البايتات | U+FEFF | تحدد الترميز في بداية الملف | العمود الأول في CSV وتحليل JSON |
| واصل الكلمات | U+2060 | يمنع الكسر من دون عرض | البحث |
| علامة اليسار إلى اليمين | U+200E | تحدد اتجاه النص | ترتيب العرض والقص |
| علامة اليمين إلى اليسار | U+200F | تحدد اتجاه النص | ترتيب العرض |
| فرض اتجاه RTL | U+202E | يفرض العرض من اليمين إلى اليسار | انتحال أسماء الملفات |
| فاصل الأسطر | U+2028 | كسر سطر في Unicode | السلاسل النصية في JavaScript |
| فاصل الفقرات | U+2029 | كسر فقرة في Unicode | JSON في المحرّلات القديمة |
لكل محرف من هذه وظيفة حقيقية. المشكلة ليست في وجودها أصلًا، بل في أنها تنتقل بصمت بين أنظمة تتعامل معها بطرق مختلفة.
من أين تأتي
- النسخ من صفحات الويب. تُدرج المواقع مسافات صفرية للتحكم في مواضع التفاف السلاسل الطويلة، وواصلات ناعمة للنص المضبوط. وكلاهما ينتقل مع النص المنسوخ.
- معالجات النصوص. تُدرج المسافات غير الفاصلة تلقائيًا لإبقاء الرقم مع وحدته أو الاسم في سطر واحد.
- الاستخراج من PDF. ينتج عن النسخ من ملف PDF واصلات ناعمة في المواضع التي كسر فيها المصمم الكلمات بين الأسطر، إضافة إلى مسافات غير فاصلة من تباعد النص المضبوط.
- واجهات محادثات الذكاء الاصطناعي. يُعرض الناتج كـ HTML، فيحمل النسخ محارف التباعد التي استخدمها ذلك التنسيق، وهي عادة U+00A0 وU+202F.
- تحويلات الترميز. تظهر علامة ترتيب البايتات عند حفظ ملف بصيغة UTF-8 مع BOM، وهو أمر شائع على Windows.
- الإدراج المتعمد. استُخدمت المحارف الصفرية لبصمة المستندات بهدف تتبع التسريبات، ولتمرير نصوص عبر مرشحات الكلمات المفتاحية.
النمط ثابت: تدخل هذه المحارف عندما ينتقل النص من سياق عرض إلى سياق بيانات.
كيف تعطّل الأمور
يفشل البحث بصمت. وجود مسافة صفرية داخل كلمة يعني أن البحث عنها لا يرجع شيئًا. ولا يوجد أي مؤشر على السبب، فالكلمة ظاهرة أمامك بوضوح.
يرفض التحقق مدخلات صحيحة. عنوان بريد إلكتروني تنتهي به مسافة غير فاصلة لن يجتاز فحص الصيغة. يرى المستخدم عنوانًا صحيحًا ورسالة خطأ لا معنى لها.
تفشل المقارنات. العبارة "Ada" === "Ada\u200B" نتيجتها false. تفوت إزالةُ التكرارات تكراراتٍ واضحة، ولا ترجع عمليات البحث شيئًا، وتسقط صفوف من الربط.
القص لا يفيد. معظم تطبيقات trim() تزيل مسافات ASCII فقط. تنجو المسافة غير الفاصلة من .trim() في لغات كثيرة، ولهذا تتكرر عبارة «لقد قصصتها أصلًا» كطريق مسدود.
الشيفرة لا تُترجم. مسافة غير فاصلة حيث يجب أن تكون مسافة عادية تنتج خطأ صياغة في سطر يبدو سليمًا تمامًا.
يُتلف استيراد CSV. تلتصق علامة ترتيب البايتات بالترويسة الأولى، فيصبح id كالتالي \uFEFFid ويفشل العمود الأول في التطابق بصمت.
الطول غير صحيح. الواصلات الصفرية في تسلسلات الرموز التعبيرية تعني أن رمزًا تعبيريًا مرئيًا واحدًا قد يتكون من عدة نقاط ترميز، فترفض حدود المحارف مدخلات تبدو ضمن الحد.
أسماء الملفات قد تخدع. يعكس U+202E النص المعروض، فيمكن أن يظهر report\u202Egnp.exe كأنه reportexe.png. هذه مشكلة أمنية حقيقية وليست مجرد غرابة تقنية.
العثور عليها وإزالتها
افحص أولًا. الصق النص في فاحص المحارف غير المرئية. يعرض الأنواع الموجودة وعدد كل منها، ثم يزيلها عند الطلب. رؤية الأعداد أولًا تخبرك هل تواجه محرفًا شاردًا واحدًا أم تلوثًا ممنهجًا.
في المحرر. يُبرز VS Code معظم المحارف غير المرئية افتراضيًا عبر إعداد unicode-highlight. ولميزة البحث والاستبدال الداعمة للتعابير النمطية، يمسك هذا النمط المجموعة الشائعة:
[\u200B-\u200F\u00AD\uFEFF\u2060\u202A-\u202E\u2028\u2029]
في الشيفرة، طبّق التوحيد عند الحدود، أي عند وصول البيانات لا عند استخدامها:
const clean = input
.replace(/[\u200B-\u200F\u2060\uFEFF\u00AD\u202A-\u202E]/g, "")
.replace(/[\u00A0\u202F]/g, " ")
.normalize("NFC")
.trim();
لاحظ المعالجة على خطوتين: المحارف الصفرية تُحذف، بينما المسافات غير الفاصلة تُحوَّل إلى مسافات عادية، لأن حذفها سيلصق الكلمات ببعضها.
لا تحذف بعمى. الواصلات الصفرية عنصر أساسي في تسلسلات الرموز التعبيرية وفي العربية والفارسية وكثير من الأبجديات الهندية، فإزالتها تغيّر طريقة عرض الكلمات. احذف بصرامة في المعرّفات والمفاتيح والشيفرة، واحذف بحذر في المحتوى الموجه للمستخدم.
وللسؤال المحدد حول ما إذا كانت أدوات الذكاء الاصطناعي تُدرج هذه المحارف عمدًا، راجع هل يخفي ChatGPT محارف غير مرئية. أما المكافئ المرئي الذي يعطّل بالقدر نفسه فيشرحه مقال لماذا تعطّل علامات الاقتباس المنحنية الشيفرة.
الأسئلة الشائعة
ما المسافة الصفرية؟
هي U+200B، محرف لا يشغل أي عرض مرئي لكنه يبقى جزءًا من السلسلة النصية. تستخدمه المواقع لاقتراح مواضع التفاف الكلمات الطويلة. ويعطّل البحث ومقارنة السلاسل لأن النص يبدو مطابقًا بينما تختلف البايتات الفعلية.
لماذا يفشل بحثي عن نص موجود بوضوح؟
يوجد شبه مؤكد محرف غير مرئي داخل الكلمة، غالبًا مسافة صفرية أو واصلة ناعمة التُقطت من صفحة ويب أو ملف PDF. النص المعروض يطابق ما كتبته، لكن السلسلة الفعلية لا تطابقه.
هل تزيل trim() المسافات غير الفاصلة؟
عادة لا. معظم تطبيقات trim تزيل مسافات ASCII فقط، فتنجو المسافة غير الفاصلة (U+00A0). لهذا يظل إدخال يبدو خاليًا من المسافات الزائدة يرفضه التحقق حتى بعد القص.
كيف أزيل المحارف غير المرئية من النص؟
الصقه في فاحص يسردها ويزيلها، أو استخدم تعبيرًا نمطيًا يغطي U+200B–U+200F وU+00AD وU+FEFF وU+2060 وU+202A–U+202E. احذف المحارف الصفرية، لكن حوّل المسافات غير الفاصلة إلى مسافات عادية بدل حذفها وإلا التصقت الكلمات ببعضها.
هل للمحارف غير المرئية فائدة يومًا؟
نعم. تتحكم الواصلات الناعمة في كسر الكلمات في النص المضبوط، وتبقي المسافات غير الفاصلة القيم مع وحداتها، والواصلات الصفرية ضرورية لتسلسلات الرموز التعبيرية وللأبجديات العربية والفارسية والهندية. احذف بصرامة في الشيفرة والمعرّفات، وبحذر في النصوص العادية.