أحيانًا تفتح ملفين فلا تجد بينهما اختلافًا في حرف واحد، ومع ذلك يقول diff إن كل الأسطر تغيّرت، أو إن السطر الأول أو الأخير فقط مختلف. السبب في الغالب أحرف لا تظهر على الشاشة. يلخّص هذا المقال أنواع هذه «الفروق غير المرئية» وطرق حلها.
حروف فاصل السطر: CRLF وLF وCR
تختلف أعراف الانتقال إلى سطر جديد من نظام تشغيل إلى آخر.
| الرمز | البايتات | أين يُستخدم غالبًا |
|---|---|---|
| LF | 0A (\n) | Linux وmacOS ومعظم أدوات التطوير |
| CRLF | 0D 0A (\r\n) | برامج كثيرة في Windows مثل المفكرة، وبروتوكولات الإنترنت مثل البريد الإلكتروني وHTTP |
| CR | 0D (\r) | إصدارات Mac OS القديمة جدًا (قبل الإصدار 9) |
إذا حُفظ ملف بـ CRLF وآخر بـ LF، ففي المقارنة سطرًا بسطر يُحتسب حرف \r غير المرئي في نهاية كل سطر فرقًا، فيبدو كأن الملف كله تغيّر. يمكن تجاهل هذا الفرق في GNU diff بالخيار --strip-trailing-cr، وفي git diff بالخيار --ignore-cr-at-eol.
السطر الجديد في نهاية الملف
وفق أعراف POSIX تنتهي أسطر الملف النصي بحرف سطر جديد. لذلك تضع أدوات كثيرة سطرًا جديدًا بعد السطر الأخير أيضًا، لكن بعض المحررات لا تفعل. يظهر هذا الفرق في unified diff على النحو التالي.
@@ -1,2 +1,2 @@ host=example.com-port=8080\ No newline at end of file+port=8080
\ No newline at end of file تعني أنه لا يوجد سطر جديد في نهاية السطر الذي فوقها مباشرة. في المثال أعلاه المحتوى واحد، وأُضيف السطر الجديد الأخير في الملف بعد التغيير فقط. تشترط أدلة أسلوب كتابة الكود كثيرًا وجود سطر جديد في نهاية الملف، ويمكن توحيد ذلك بإعداد «إدراج سطر جديد في نهاية الملف» في المحرر.
BOM: عندما يختلف السطر الأول فقط
قد تُضاف في بداية ملف UTF-8 ثلاثة بايتات هي EF BB BF. تُسمّى هذه علامة ترتيب البايتات (BOM). لا حاجة إلى ترتيب البايتات في UTF-8، لكن بعض البرامج تضيفها علامةً على أن «هذا الملف بترميز UTF-8». لا تظهر BOM على الشاشة، فإذا قيل إن السطر الأول فقط مختلف فاشتبه في BOM. يزيل هذا الموقع BOM من بداية الملف عند تحميله، وإذا فعّلت خيارات تجاهل المسافات فإنه يعامل BOM (U+FEFF) داخل النص الملصوق كمسافة أيضًا.
علامات الجدولة والمسافات والمسافات الخاصة
- علامة الجدولة والمسافات: قد تبدو علامة جدولة واحدة وأربع مسافات متماثلة على الشاشة، لكنها أحرف مختلفة.
- المسافة غير القابلة للكسر (NBSP، U+00A0): كثيرًا ما تدخل في النصوص المنسوخة من صفحات الويب أو معالجات النصوص.
- المسافة كاملة العرض (U+3000): قد تُدخلها أدوات الإدخال الصينية واليابانية والكورية.
- المسافة صفرية العرض (U+200B): عرضها صفر فلا تُرى إطلاقًا، لكنها تُحسب حرفًا.
تُستبعد معظم هذه الأحرف عند تفعيل خيارات تجاهل المسافات (لكن الأحرف التي لا تُصنَّف مسافات، مثل U+200B، قد تبقى حتى مع تفعيل الخيار). وإذا حوّلت المقارنة داخل السطر إلى أحرف، يُبيّن لك الإبراز موضع الحرف غير المرئي.
توحيد يونيكود: الحرف نفسه ببايتات مختلفة
يمكن كتابة الحرف «é» كحرف واحد (U+00E9)، أو كحرف e (U+0065) تتبعه علامة النبر المركّبة (U+0301). وكذلك المقطع الكوري «가» قد يكون حرفًا واحدًا (U+AC00) أو تركيبًا من حرفين منفصلين (U+1100 وU+1161). يسمّي يونيكود الشكل الأول NFC والثاني NFD (UAX #15). يبدو النصان متطابقين على الشاشة، لكنهما عند المقارنة أحرف مختلفة. ومن الأمثلة الشائعة أسماء الملفات التي أُنشئت على macOS، إذ تصل أحيانًا إلى أنظمة أخرى بالشكل المفكك فلا تتطابق مع أسماء تبدو مماثلة لها. الحل هو تحويل الجهتين إلى NFC قبل المقارنة.
توحيد فواصل الأسطر في git
إذا كان المستودع يُعمل عليه من أنظمة تشغيل مختلفة، فيمكن توحيد فواصل الأسطر بإعدادات git.
core.autocrlf: عند ضبطه علىtrueيحوّل LF إلى CRLF عند checkout، ويحوّل CRLF إلى LF عند commit (غالبًا على Windows). أماinputفيحوّل CRLF إلى LF عند commit فقط..gitattributes: يحدد القواعد على مستوى المستودع، فتبقى النتيجة واحدة حتى لو اختلفت إعدادات الأشخاص.
* text=auto
*.sh text eol=lf
*.bat text eol=crlf
يوحّد text=auto فواصل الأسطر إلى LF داخل المستودع للملفات التي يعدّها git نصية، ويحدد eol=lf وeol=crlf فاصل السطر عند إخراج الملفات إلى مجلد العمل.
جرّبها في هذه الأداة
خيار توحيد نهايات الأسطر في مقارنة النصوص مفعّل افتراضيًا، فيعدّ CRLF وCR وLF فاصل سطر واحدًا ويتجاهل وجود السطر الجديد في نهاية الملف من عدمه. إذا أوقفته يظهر CR في نهاية السطر على هيئة ␍، والسطر الذي ليس بعده سطر جديد على هيئة ∅، فتستطيع رؤية الفروق التي كانت مخفية. خيارات المسافات مشروحة في خيارات تجاهل المسافات، ومعاني رموز النتيجة في كيف تقرأ unified diff.