Skip to content
العودة إلى المدوّنة
دروس تعليمية

مقارنة النصوص أونلاين: شرح خوارزمية LCS/Myers

قارن نصّين أونلاين بعرض جنبًا إلى جنب وصيغة unified diff، ومتى تختار text أو JSON diff لمراجعة الكود.

14 دقائق للقراءة

مقارنة النصوص أونلاين: خوارزمية LCS/Myers و6 حالات استخدام

تجيب أداة مقارنة النصوص أونلاين عن سؤال واحد بسرعة: ما الذي تغيّر فعلًا بين النسخة A والنسخة B؟ تلصق كتلتي نص، فتشغّل الأداة خوارزمية أطول تتابع مشترك (Longest Common Subsequence)، وتحصل على عرض جنبًا إلى جنب أو موحَّد لكل إدراج وحذف وتعديل، عادةً في أقل من ميلي ثانية.

هذا الدليل موجَّه إلى المطوّرين الذين يراجعون الكود، ومهندسي SRE الذين يقارنون شرائح من السجلات، والمحامين الذين يراجعون العقود، والكتّاب الذين يراجعون التعديلات. يغطي الدليل الخوارزمية (LCS وMyers وPatience)، والعرضين القياسيين، وخيارات التجاهل التي تحلّ 95٪ من شكاوى «كل شيء يبدو متغيّرًا»، ومتى ينبغي اللجوء إلى مقارنة JSON بدلًا من ذلك، وست حالات استخدام جاهزة للنسخ واللصق، والمزالق التي تفسّرها الخوارزمية نفسها.

تريد القفز مباشرة لمقارنة نصّين الآن؟ افتح مقارنة النصوص. يعمل بالكامل داخل متصفحك، بلا أي رفع.

1. ما هي مقارنة النصوص (Text Diff

مقارنة النصوص هي أصغر مجموعة من الإدراجات والحذوفات التي تحوّل نصًّا إلى آخر، مع وسم كل سطر بأنه مُضاف أو محذوف أو دون تغيير. تضيف أدوات المقارنة الحديثة تمريرة ثانية على مستوى الكلمة أو الحرف، فيُضاء توكِن واحد فقط عند تغيير حرف واحد بدل إضاءة السطر كاملًا.

1.1 لماذا لا تكفي مساواة الأحرف (===

أدرج سطرًا واحدًا في أعلى ملف إعداد من 200 سطر، فستُبلِّغ المقارنة الساذجة حرفًا بحرف عن كل بايت بعد نقطة الإدراج بوصفه مختلفًا. النص لم يتغيّر، موقعه فقط هو الذي تغيّر. على خوارزمية المقارنة أن تدرك أن «الـ199 سطرًا التالية هي ذاتها، لكنها أُزيحت بمقدار سطر واحد» وأن تُبلّغ عن إدراج واحد فقط. هذا الإدراك هو ما يمنحك إيّاه LCS، ولهذا تشحن أدوات git وGitHub وكل أدوات مراجعة الكود نسختها منه.

1.2 العرض جنبًا إلى جنب مقابل unified diff

العرض جنبًا إلى جنب يضع النسختين في عمودين متوازيين ويلوّن الخلايا: أخضر للمُضاف، أحمر للمحذوف، أصفر للمعدَّل. أما unified diff فهو الصيغة النصية الأقدم من GNU diff: عمود واحد، علامتا - و+، وثلاثة أسطر من السياق حول كل كتلة (hunk). المقارنة ذاتها، عرضان مختلفان. يغطي القسم 4 متى تستخدم كلًّا منهما.

1.3 أين تُستخدم مقارنة النصوص

مراجعة الكود على GitHub وGitLab. مخرجات git diff محليًا. الباتشات الملصوقة في Slack. مراجعة العقود (redlining). مراجعة الترجمة. اختبارات snapshot في CI التي تفشل بمخرجات +/-. تحقيق الجدول الزمني للسجلات. مقارنة ملفي .env. أي مكان تحتاج فيه إلى مطابقة كتلتي نص سطرًا بسطر.

افتح مقارنة النصوص والصق نصّين لترى كل ذلك حيًّا. كل مقارنة تجري محليًا داخل متصفحك.

2. الخوارزمية وراء مقارنة النصوص (LCS + Myers + Patience)

2.1 أطول تتابع مشترك (Longest Common Subsequence)

لدينا متسلسلتان من الأسطر A وB، وأطول تتابع مشترك هو أطول قائمة من الأسطر التي تظهر في كليهما، بالترتيب نفسه، دون اشتراط التجاور. بمجرد امتلاك LCS، تصبح المقارنة مباشرة: الأسطر الموجودة في A وليست في LCS تُعدّ محذوفة، والأسطر الموجودة في B وليست في LCS تُعدّ مُضافة، والأسطر التي يحتويها LCS دون تغيير.

تعمل LCS الكلاسيكية بجدول برمجة ديناميكية حجمه N × M. الخلية (i, j) تخزّن طول LCS لأول i سطر من A وأول j سطر من B. تملأ الجدول من اليسار إلى اليمين ومن الأعلى إلى الأسفل، ثم تسير من الخلية اليمنى السفلى عائدًا لإعادة بناء سيناريو التحرير. الزمن والحيّز كلاهما O(N×M): جيد لملفين بألف سطر، بطيء لسجل بمئة ألف سطر.

2.2 Myers (1986)

تُعيد ورقة Eugene Myers عام 1986 «An O(ND) Difference Algorithm and Its Variations» صياغة المشكلة بوصفها أقصر مسار في رسم بياني للتحرير: العقد هي المواقع (i, j) في المدخلين، والحركات الأفقية حذوفات، والعمودية إدراجات، والقطرية تطابقات. أقصر مسار هو أقل سيناريو تحرير.

تعمل Myers بزمن O((N+M)D)، حيث D هو حجم سيناريو التحرير. حين يكون النصّان متشابهين (وهو الوضع المعتاد للمقارنات)، يكون D صغيرًا والخوارزمية خطية فعليًا. هذه هي الخوارزمية الافتراضية في git diff وGNU diff ومُصيِّر طلبات الدمج في GitHub. وفي 99٪ من مدخلات الويب، هي الإجابة الصحيحة.

2.3 Patience diff (Bram Cohen, 2005)

تتّبع Patience diff نهجًا مختلفًا: ابحث عن الأسطر التي تظهر مرة واحدة بالضبط في كل مدخل (تُسمَّى «أسطر الإرساء الفريدة»)، طابِق بينها، ثم استدعِ الخوارزمية تكراريًا على الفجوات بين المراسي. الرياضيات أعقد (والحد الأسوأ يظل سيئًا)، لكن المخرجات أسهل قراءةً بكثير على الكود.

لماذا؟ لأن Myers تُصغِّر مسافة التحرير، وهو أمر رياضيًا أمثل لكنه بصريًا فظيع حين يَعبر التراصف الأمثل أقواسًا أو أسطرًا فارغة لا علاقة لها بالموضوع. أما Patience فترفض المحاذاة على الجاهز المتكرّر (كل ملف فيه أسطر }، وكل ملف فيه أسطر فارغة)، فتبقى حدود الدوال سليمة. اخترعها Bram Cohen لأجل Bazaar، ويشحن Git دعمها عبر git diff --patience. خوارزمية Histogram القريبة منها (git diff —histogram) أسرع قليلًا بجودة مخرجات مماثلة.

تخيّل نسختين من الملف نفسه نُقلت فيه دالة. قد تُحاذي Myers القوس المُغلِق للدالة A مع القوس المُغلِق للدالة B وتُبلّغ بأن المحتوى مختلف كليًّا. تربط Patience بأسماء الدوال الفريدة وتُبلّغ بنقل نظيف. المدخل نفسه، تجربة مراجعة مختلفة تمامًا.

2.4 مقارنة الخوارزميات

الخاصيةMyers (الافتراضية)PatienceHistogram
التعقيد الزمنيO((N+M)D)~O(N log N) في الحالة الشائعةمشابه لـPatience
مسافة تحرير مثلىنعم، أقصر سيناريولا، قد تكون أطوللا، قد تكون أطول
تُقرأ بشكل طبيعي على الكودأحيانًا تخلط الأقواس والأسطر الفارغةممتازة، ترتكز على أسطر فريدةممتازة
تستخدمهاgit افتراضيًا، GNU diff، واجهة GitHubgit diff —patience، Bazaargit diff —histogram
الأنسب لـالسرعة والصواب على معظم المدخلاتمراجعات الكود وفوارق إعادة الهيكلةكما Patience، أسرع قليلًا

2.5 ماذا تفعل هذه الأداة

تستخدم مقارنة النصوص خوارزمية LCS الكلاسيكية بالبرمجة الديناميكية، مع تحسينين قويين: قصّ السوابق واللواحق المشتركة، وتمريرة ثانية بـLCS على مستوى التوكِن لمقارنة الكلمات داخل السطر. مقارنة ملفي إعداد من ألفي سطر مع تغيير سطر واحد تنهار إلى جدول DP بحجم 1×1 بعد القصّ وتُصيَّر في أقل من ميلي ثانية. لمدخلات الويب النمطية يكون الاختيار بين Myers وDP غير مرئي، فكلاهما ينتهي أسرع مما يستطيع المتصفح رسم النتيجة.

3. مقارنة الكلمات داخل السطر: لماذا تُضيء تغيير حرف واحد السطر بأكمله

تُغيّر معرّفًا واحدًا في سطر، فيشتعل السطر كله بالأحمر والأخضر. خلل؟ لا، بل تصميم.

تشغّل المقارنة LCS أولًا على مستوى السطر: «استُبدِل السطر 14». ثم لكل زوج مستبدَل تشغّل LCS ثانيةً على مستوى التوكِن. تُنتَج التوكِنات بالتقسيم وفق حدود الكلمات في Unicode: تبقى متتاليات الأحرف والأرقام معًا، ويصبح كل فاصل أو علامة ترقيم توكِنًا مستقلًا. تمنحك التمريرة الثانية أقل سيناريو تحرير على مستوى التوكِن داخل ذلك السطر.

يرسم المُصيِّر السطر كاملًا بلون الإبراز كي تجده عينك، ثم يلوّن التوكِنات المتغيّرة فقط بخلفية لامعة. أما التوكِنات غير المتغيّرة حولها فتحمل نسخة باهتة من اللون نفسه، فتبقى حاضرة لكن هادئة بصريًا. تهبط عينك على التحرير الفعلي بالضبط.

مثال 1: إعادة تسمية معرّف. تصبح function getUser(id) هي function getUser(userId). السطر بأكمله موسوم بالتعديل. داخل السطر، يحمل id فقط (مشطوبًا بالأحمر) وuserId (أخضر لامع) الإبراز السطري. كل ما سواهما يبقى باهتًا.

مثال 2: تغيير زمن استجابة في السجل. يصبح POST /api/orders 201 88ms هو POST /api/orders 201 4200ms. السطر مُعدَّل. سطريًا، فقط 88 و4200 لامعان. يبقى المسار والـmethod ورمز الحالة باهتًا، وهذا بالضبط ما يحتاجه قارئ خط زمني للحوادث.

عندما تتغيّر توكِنات كثيرة، يصبح إبراز مستوى الكلمات ضوضاءً. ترتدّ الأداة عندئذٍ إلى عرض زوجي «حذف ثم إضافة»: السطر الأصلي معروضًا كمحذوف، والسطر الجديد كمُضاف، دون تلوين داخلي. العتبة تقريبًا «أكثر من نصف التوكِنات مختلف».

الخلاصة: مقارنة مستوى السطر تخبرك أيّ سطر تغيّر، ومقارنة مستوى الكلمات تخبرك أيّ أحرف على ذلك السطر تحمل التغيير. انقر على «Sample» داخل مقارنة النصوص لترى كلا العرضين على المدخل نفسه.

4. Side-by-Side مقابل Unified Diff: عرضان لمقارنة واحدة

4.1 العرض جنبًا إلى جنب

عمودان: الأصلي على اليسار، والمُعدَّل على اليمين. الأسطر المتطابقة محاذاة أفقيًا. الأسطر المُضافة تظهر في العمود الأيمن فقط بخلفية خضراء؛ والمحذوفة تظهر في العمود الأيسر فقط بخلفية حمراء؛ والأزواج المُعدَّلة تجلس متجاورة مع شريط أصفر وإبراز كلمات داخل السطر.

استخدم العرض جنبًا إلى جنب حين يقرأ المقارنةَ إنسان: مراجعة PR، أو تعليم، أو عرض توضيحي، أو شرح تغيير عقد لطرف غير تقني. هذا هو العرض المُعَدّ للقراءة المباشرة.

العيب: لا يَنتقل. لا يمكنك لصق عرض جنبًا إلى جنب في Slack وتطبيقه من قِبل أي شخص. ولا يمكنك تمريره إلى patch. للمشاركة والتطبيق، تحتاج إلى unified.

4.2 صيغة unified diff

Unified diff صيغة نصية ظهرت قبل عقود، عرّفتها GNU diff ووحّدتها POSIX. مثال كامل:

--- original
+++ modified
@@ -1,3 +1,4 @@
 1. The service is provided as-is.
 2. Either party may terminate with 30 days notice.
+2a. Termination notice must be in writing.
 3. Disputes are resolved in California courts.

السطران الأولان يسمّيان الملفين المصدريين. السطر @@ -L,C +L,C @@ ترويسة كتلة (hunk header): -L,C يعني ابتداءً من السطر L من الأصلي، تشارك C سطرًا؛ و+L,C يعني الشيء نفسه للنسخة المُعدَّلة. داخل الكتلة، الأسطر التي تبدأ بمسافة فارغة هي سياق (دون تغيير)، و- محذوف، و+ مُضاف.

ثلاثة أسطر من السياق فوق كل تغيير وتحته هي الإعداد الافتراضي لـGNU. تتيح معظم الأدوات تغييره عبر -U n: diff -U0 بلا سياق، وdiff -U10 لعشرة أسطر. تتعقّب ترويسة الكتلة أيّ قيمة اخترتها.

في مقارنة النصوص، انقر تبويب Unified للتبديل بين العرضين أو انقر Copy unified diff لنسخ الباتش إلى الحافظة.

4.3 أين يتنقّل unified diff

يتنقّل unified diff أينما ذهبت، فمعظم الأدوات تقرؤه مباشرة.

الوجهةيقبل unified diff؟كيف
GNU patchنعمpatch -p1 < diff.patch
git applyنعمgit apply diff.patch
تعليق مراجعة PR في GitHubنعم (داخل كتلة ```diff )يُصيَّر مع ألوان
تعليق MR في GitLabنعمالكتلة المُسوَّرة ذاتها
Bitbucket / Azure DevOps PRنعمالكتلة المُسوَّرة ذاتها
لصق في Slack / Discordجزئيًايُصيَّر نصًّا في كتلة كود، بلا ألوان
VS Code «Open Patch»نعمطبّق الباتش عبر Source Control
نص قضية Jira / Linearجزئيًايعمل داخل كتلة كود، بلا زر تطبيق

الأسطر التسعة نفسها من نصّ ---/+++/@@ تُطبَّق على patch وعلى git apply، وتُصيَّر في ثلاث منصّات PR، وتنجو من لصقة Slack. لا توجد صيغة مقارنة أخرى لها مدى قريب من هذا.

4.4 متى تختار كلًّا منهما

العرض جنبًا إلى جنب للمراجعة، وunified للمشاركة والتطبيق. إن كنت تقرأ المقارنة بنفسك، فالعمودان أسرع. إن كان أيّ شخص أو أداة في المصبّ تحتاج إلى استهلاكها (مراجع، أو أداة، أو أمر patch)، فانسخ بصيغة unified.

5. خيارات التجاهل: المسافات البيضاء، حالة الأحرف، الأسطر الفارغة، نهايات الأسطر

معظم شكاوى «كل شيء يبدو متغيّرًا» ضوضاء. أربعة مفاتيح تحلّ 95٪ منها.

  1. تجاهل حالة الأحرف يطابق A مع a. يكافئ git diff -i. استخدمه لمقارنات متغيّرات البيئة، وتدقيق أسلوب كلمات SQL المفتاحية، وأي مكان يكون فيه العرف بين الأحرف الكبيرة الصاخبة والصغيرة الهادئة، لكن المعنى متطابقًا.
  2. تجاهل كل المسافات البيضاء يطوي كل مسافة وتبويب وسطر جديد قبل المقارنة. يكافئ git diff -w. هذا علاج إعادة تنسيق التبويب ↔ المسافات، وإعادة كتابة المسافة البادئة، وفوارق «اعتمدنا Prettier» التي تدمّر إحصاء الأسطر. مقارنة تتجاهل المسافات البيضاء على هذه التغييرات تنخفض عادةً من 87 تعديلًا إلى 4.
  3. تجاهل المسافات والتبويبات اللاحقة يجرّد المسافات البيضاء في نهاية السطر فقط. يكافئ git diff -b. علاج ضوضاء CRLF بعد نسخ بين أجهزة Windows وUnix: تُرشَّح أحرف \r اللاحقة ويتراصف المحتوى الفعلي.
  4. تجاهل الأسطر الفارغة يُسقط الأسطر الفارغة قبل المقارنة. علاج «أضفتُ فاصلًا بين فقرتين، فبدت الفقرة 12 مختلفة كليًّا» في مقارنات النثر.

ملف إعداد من 200 سطر يُبلّغ بـ«87 تعديلًا» يهبط عادةً إلى «4 تعديلات» بعد تجاهل كل المسافات البيضاء. نسخة Windows إلى Unix تُؤشّر كل سطر تهبط إلى الصفر مع تجاهل المسافات اللاحقة. كل مفتاح مستقل ويُحفظ بين الجلسات.

CRLF مقابل LF. نهايات أسطر Windows هي \r\n؛ وUnix هي \n؛ وMac الكلاسيكي هو \r. افتح ملف Windows في محرّر Unix لا يطبّع، فيبقى \r اللاحق. كل سطر سيُقارن بأنه «المحتوى مطابق لكن في نهايته \r». تجاهل المسافات اللاحقة يُسكت هذه الضوضاء دون خسارة التغييرات الحقيقية.

تحذير واحد. خيارات التجاهل تُخفي تغييرات حقيقية أيضًا. فعِّل تجاهل حالة الأحرف، فيبدو إعادة هيكلة تحوّل LOG.error إلى log.Error متطابقة. فعِّل تجاهل كل المسافات البيضاء، فيصبح خطأ في إزاحة Python غير مرئي. اختر المفاتيح المناسبة للسؤال الذي تطرحه، ثم أطفئها عند الانتهاء.

6. مقارنة النصوص مقابل JSON diff مقابل git diff: مصفوفة قرار

مقارنة النصوص هي مطابقة سطر-وكلمة دون فهم للبنية. هذا بالضبط ما تريده للنثر، وبالضبط ما لا تريده لـJSON.

6.1 مصفوفة القرار

نوع المدخلText diffJSON diffGit diff
نثر / Markdown / عقدالأفضلالأداة الخاطئةجزئي (يعمل على الملفات المتعقَّبة فقط)
قصاصة كود (لصق ملف واحد)الأفضلالأداة الخاطئةجزئي (يحتاج مستودعًا)
كود في مستودع (متعدد الملفات)جزئيالأداة الخاطئةالأفضل
استجابة API JSONالأداة الخاطئة (إيجابيات كاذبة لترتيب المفاتيح)الأفضلالأداة الخاطئة
إعداد YAML / TOMLجزئي (إيجابيات كاذبة لترتيب المفاتيح)الأفضل (بعد التحويل)جزئي
CSV صفًّا بصفجزئيالأداة الخاطئةالأداة الخاطئة
سجلّ / heredocالأفضلالأداة الخاطئةالأداة الخاطئة
ملف ثنائيالأداة الخاطئةالأداة الخاطئةgit diff —binary

6.2 متى تكون مقارنة النصوص هي الأداة الخاطئة

ثلاثة أخطاء كلاسيكية.

JSON بمفاتيح مُعاد ترتيبها. {"a":1,"b":2} و{"b":2,"a":1} هما المستند الجيسوني نفسه. تُبلِّغ مقارنة النصوص بأن كل سطر متغيّر لأنها بالفعل أسطر مختلفة. استخدم مقارنة JSON، فهي تفهم أن مفاتيح JSON غير مرتَّبة.

إعدادات YAML أُعيد تنسيقها. غيِّر قيمة واحدة، ومرِّر الملف عبر مُنسِّق، فتتزحزح المسافات البادئة وترتيب المفاتيح وأسلوب الاقتباس كلها. تُبلِّغ مقارنة النصوص بإعادة كتابة شاملة. حوّل الملفين إلى JSON أولًا، ثم قارن بـJSON Diff.

إعادة هيكلة متعددة الملفات مع إعادة تسمية. يتعقّب Git إعادة التسمية؛ مقارنة النصوص لا تفعل. لو قارنت شجرتين بتجميع الملفات في كتلة واحدة، فإن كل نقل عبر الملفات يظهر بوصفه محذوفًا + مُضافًا. استخدم git diff (أو git diff —find-renames=80%) عوضًا عن ذلك.

6.3 متى تكون مقارنة النصوص هي الأداة الصحيحة بالضبط

النثر. قصاصات الكود الملصوقة من أي مكان. مراجعة العقود. شرائح السجلات. مراجعة الترجمة حين تطابق جملًا باللغة الطبيعية. ملفات .env حين يهمّ الترتيب لأن الصدفات تقرؤها من أعلى لأسفل. أيّ مكان تحمل فيه الأسطر نفسها معنى.

للتعمّق في تصفية ضوضاء مقارنات JSON (الطوابع الزمنية، ومعرّفات الطلبات، وUUIDات المولَّدة تلقائيًا)، اقرأ كيفية تجاهل الطوابع الزمنية والمعرّفات في مقارنة JSON.

7. ست حالات واقعية (مع مدخلات جاهزة للنسخ واللصق)

7.1 قصاصة مراجعة كود: إعادة تسمية دالة

تراجع PR. أعاد المؤلف تسمية id إلى userId وأضاف فقرة حراسة. الصق النسختين:

// Original
function getUser(id) {
  const u = db.users.find(x => x.id === id);
  return u;
}
// Modified
function getUser(userId) {
  if (!userId) return null;
  const u = db.users.find(x => x.id === userId);
  return u;
}

تُظهر المقارنة ثلاثة أسطر مُعدَّلة بالإضافة إلى سطر مُضاف واحد. يبرز إبراز الكلمات داخل السطر كل توكِن iduserId؛ وتظهر فقرة الحراسة الجديدة بخلفية خضراء. خيارات التجاهل مُطفأة. جرّب هذا في مقارنة النصوص وانسخ مخرجات unified لتتركها تعليقًا في المراجعة.

7.2 مراجعة عقد أو سياسة: بند واحد مُدرَج

خمسون فقرة عقد، وبند واحد مُدرَج. الصق نسخة الأمس على اليسار ونسخة اليوم على اليمين:

1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
3. Disputes are resolved in California courts.
1. The service is provided as-is.
2. Either party may terminate with 30 days notice.
2a. Termination notice must be in writing.
3. Disputes are resolved in California courts.

تُصيِّر المقارنة 49 سطرًا دون تغيير وسطرًا واحدًا مُضافًا (+2a. Termination notice must be in writing.). صدِّر unified diff أثرًا للمراجعة القانونية.

7.3 تحقيق الجدول الزمني للسجلات

تشكّ في انحدار في زمن الاستجابة. خذ شريحة من سجلات الوصول قبل الحادث وأثناءه:

GET /api/users 200 14ms
POST /api/orders 201 88ms
GET /api/orders/42 200 21ms
GET /api/users 200 14ms
POST /api/orders 201 4200ms
GET /api/orders/42 500 21ms

يبرز الإبراز السطري 884200 (قفزة زمن استجابة 50 ضعفًا) و200500 (نقطة نهاية تفاصيل الطلب بدأت تفشل). لعمل أعمق على السجلات (استخراج الحقول، والتجميع حسب نقطة النهاية، وحساب الـpercentiles)، اقرن المقارنة بـمرجع jq السريع إن كانت سجلاتك بصيغة JSON.

7.4 مراجعة ترجمة: الحفاظ على العناصر النائبة

استأجرت وكالة ترجمة جديدة وتريد التحقق من أن النسخة الجديدة تطابق القديمة في البنية. الصق الترجمة القديمة على اليسار، والجديدة على اليمين. فعِّل تجاهل المسافات والتبويبات اللاحقة لأن المترجمين كثيرًا ما يضيفون مسافة شاردة في نهاية السلاسل.

تؤكّد المقارنة بقاء كل عنصر نائب من نوع {username} و{count} و%s في مكانه، وأن نصّ اللغة الطبيعية وحده يتغيّر. يظهر العنصر النائب المفقود بوصفه توكِنًا محذوفًا في المقارنة السطرية، فتلتقطه قبل الشحن. إن احتجت إلى مقارنة صيغ العناصر النائبة ذاتها، فإن مرجع التعبيرات النمطية يغطي \{\w+\} وأخواته. جرّب هذا في مقارنة النصوص.

7.5 تدقيق إعداد أو .env: الإنتاج مقابل التهيئة

قارن ملفي .env. فعِّل تجاهل الأسطر الفارغة كي لا يخرج التجميع الفقري الأسلوبي عن المحاذاة. تُريك المقارنة أيّ مفاتيح تختلف قيمها، وأيّ مفاتيح موجودة في بيئة دون أخرى، وأين انحرفت التعليقات. خمس دقائق تمنع جلسة تنقيح «يعمل في التهيئة لكنه لا يعمل في الإنتاج».

7.6 مراجعة نثر أو مسوّدة

أعاد إليك المحرّر مسوّدة. الصق الأصل على اليسار والنسخة المُحرَّرة على اليمين. تُريك مقارنة الكلمات داخل السطر بالضبط أيّ جمل أُعيدت كتابتها، وأيّها لم تُلمَس، وأيّ فقرات أُدرجت. اقبل التغييرات أو ارفضها واحدًا تلو الآخر، دون «Track Changes»، ودون ملف Word، ودون صيغة احتكارية.

8. مزالق شائعة وكيف تقرؤها كأعراض

سلوك الخوارزمية يفسّر معظم آلام المستخدم. خمس شكاوى شائعة وما تعنيه فعلًا.

المزلق 1: «كل سطر أحمر بعد نسخ من Windows إلى Unix». العَرَض: كل سطر في المقارنة يظهر كمتغيّر رغم أن المحتوى يبدو متطابقًا. السبب: أحرف \r لاحقة من نهايات أسطر CRLF. الحلّ: فعِّل تجاهل المسافات والتبويبات اللاحقة. ستهبط المقارنة إلى التغييرات الحقيقية.

المزلق 2: «لصقتُ JSON فظهر 100٪ من الأسطر مختلفًا». العَرَض: كائنا JSON ينبغي أن يكونا متكافئين يظهران متغيّرَين كليًّا. السبب: إعادة ترتيب المفاتيح. تعامل مقارنة النصوص ترتيب الأسطر بوصفه ذا دلالة، وJSON لا يفعل ذلك. الحلّ: استخدم مقارنة JSON لأيّ مدخل من نوع JSON.

المزلق 3: «إعادة التنسيق بين التبويبات والمسافات فجّرت المقارنة». العَرَض: 87 تعديلًا كلها مسافة بادئة. السبب: غيّر مُنسِّقك المسافة البيضاء البادئة لكل سطر. الحلّ: تجاهل كل المسافات البيضاء سيطوي الضوضاء ويُظهر التغييرات الدلالية الحقيقية.

المزلق 4: «المقارنة تقول متطابق لكن cmp لا يوافق». العَرَض: تُبلِّغ المقارنة بلا فوارق لكن المقارنة على مستوى البايت تقول إن الملفين مختلفان. السبب: خيار تجاهل بقي مفعَّلًا من جلسة سابقة يخفي تغييرات حقيقية. الحلّ: افتح لوحة خيارات التجاهل وأطفئ كل المفاتيح، ثم أعد المقارنة.

المزلق 5: «تحرير قصير يظهر بوصفه حذفًا + إضافة». العَرَض: تغيير صغير يظهر بسطر محذوف منفصل وسطر مُضاف منفصل بدلًا من إبراز داخلي. السبب: تجاوزت نسبة التوكِنات المتغيّرة العتبة، فارتدّ المُصيِّر إلى عرض الأسطر المزدوجة. هذا تصميم لا خلل. بدّل إلى عرض Unified لترى زوج -/+ الكلاسيكي الذي تتوقّعه أدوات patch.

9. الخصوصية والأداء ومتى تلجأ إلى سطر الأوامر

تجري كل مقارنة في مقارنة النصوص بـJavaScript داخل متصفحك. لا رفع، ولا ملف مؤقت، ولا سجل خادم، ولا تحليلات على النص الذي تلصقه. آمن للكود الاحتكاري، والعقود الداخلية، والسجلات الخاصة، أي شيء لن تكون مستعدًّا للصقه في خادم طرف ثالث.

الحدود العملية: نحو 5,000 سطر أو 1 ميغابايت لكل طرف. تتعطّل المقارنة الحيّة فوق 200 كيلوبايت مجتمعة وتنتقل إلى زرّ مقارنة يدويّ كي لا تحجب الكتابة الصفحة. فوق 5,000 سطر يُقطع المدخل ويظهر تحذير. الحدود موجودة لأن المقارنة تجري على الخيط الرئيسي (دون web worker)، وتسليم العمل لـworker مع التسلسل سيكلّف أكثر من المقارنة نفسها على المدخلات الصغيرة.

حين يتجاوز مدخلك حدود المتصفح، اهبط إلى سطر الأوامر:

# Unified diff between two files
diff -u a.txt b.txt

# Same, but using git's diff engine (Patience, Histogram, color)
git diff --no-index a.txt b.txt
git diff --no-index --patience a.txt b.txt

# Streaming diff viewer for huge files (Rust, side-by-side, syntax-aware)
delta a.txt b.txt

بدِّل إلى سطر الأوامر للسجلات بحجم ميغابايتات، والملفات الثنائية، وفوارق المستودعات متعددة الملفات، وأيّ مكان تريد فيه تلوينًا واعيًا للنحو مثل delta، أو حيثما احتجت إلى تمرير مخرجات المقارنة إلى أداة أخرى.

10. Unicode وCJK وRTL: ملاحظات لمقارنة النصوص الدولية

يقسّم المُجزِّئ على حدود الكلمات في Unicode ضمن ثلاث فئات: متتاليات الكلمات (الأحرف \p{L} والأرقام \p{N})، وعلامات الترقيم غير الكلامية، والمسافات البيضاء. تُنتج كل فئة توكِناتها، فيصبح hello, world! خمسة توكِنات: hello، ,، ، world، !.

في محتوى CJK (الصينية، اليابانية، الكورية)، كل رمز ideograph أو kana توكِن مستقل. غيِّر حرفًا واحدًا في جملة صينية فيحمل ذلك الحرف وحده الإبراز الداخلي بينما يبقى بقية السطر باهتًا. البنية على مستوى الفقرة تبقى مبنية على الأسطر، فإعادة كتابة جملة تُضيف فاصل سطر تظهر تحريرًا سطريًا، لا تحريرًا على مستوى التوكِن.

للغات RTL (العربية، العبرية)، تستخدم المقارنة اتجاهات CSS المنطقية (ms-، me- بدل ml-، mr-). في المحليّات الـRTL ينقلب الشريط الجانبي وأعمدة الأسطر طبيعيًا؛ وداخل كل خلية مقارنة يتبع اتجاه النص المحتوى، فتُصيَّر السلاسل العربية من اليمين إلى اليسار بينما تبقى علامتا + و- محاذيتين للشريط البادئ.

يتعرّف تطبيع نهايات الأسطر على \r\n (Windows) و\n (Unix) و\r المنفرد (Mac OS القديم حتى الإصدار 9). تنقسم الأنواع الثلاثة كأسطر منفصلة، فلا ينطوي ملف مُحوَّل من منصة إلى أخرى إلى سطر عملاق واحد.

11. الأسئلة الشائعة

كيف تعمل مقارنة النصوص أونلاين؟

تقسّم مقارنة النصوص المدخلين إلى أسطر، وتشغّل خوارزمية أطول تتابع مشترك (غالبًا Myers O((N+M)D)) لإيجاد أصغر مجموعة من الإدراجات والحذوفات، ثم تُبرز الأسطر المُضافة (أخضر)، والمحذوفة (أحمر)، والباقية (رمادي). تشغّل تمريرة ثانية على مستوى التوكِن لوسم الكلمات المتغيّرة داخل كل سطر مُعدَّل. تشغّل مقارنة النصوص كامل المقارنة محليًا داخل متصفحك.

ما الفرق بين text diff وJSON diff؟

تقارن text diff سطرًا بسطر، وهي مثالية للنثر والكود والسجلات والعقود. تفهم مقارنة JSON نموذج بيانات JSON: ترتيب المفاتيح غير مهم، والأنواع صارمة (1"1")، ويمكن مطابقة المصفوفات بالمفتاح. الصق JSON في text diff فستطفو إعادة ترتيب المفاتيح أو المسافات البيضاء بوصفها تغييرات يتجاهلها JSON Diff. استخدم text diff للمحتوى غير المهيكل، وJSON Diff لاستجابات API والإعدادات.

لماذا تُظهر المقارنة أسطرًا كاملة متغيّرة وأنا لم أعدّل سوى كلمة واحدة؟

لا تفعل ذلك. يُبرَز السطر لأن شيئًا فيه تغيّر، لكن داخل الإبراز فقط التوكِنات المتغيّرة تحمل الخلفية اللامعة (أخضر للمُضاف، أحمر مشطوب للمحذوف). هذه مقارنة الكلمات داخل السطر: يبقى سياق السطر مقروءًا بينما تهبط عينك على التحرير الفعلي. حين يتغيّر من السطر ما يكفي ليصبح إبراز الكلمات غير مفيد، ترتدّ المقارنة إلى زوج «حذف ثم إضافة» منفصل حتى تبقى البنية نظيفة.

كيف أتجاهل المسافات البيضاء أو حالة الأحرف أو الأسطر الفارغة في المقارنة؟

فعِّل لوحة خيارات التجاهل. تجاهل حالة الأحرف يجعل A وa متساويين. تجاهل كل المسافات البيضاء يطوي كل مسافة وتبويب وسطر جديد، ويكافئ git diff -w. تجاهل المسافات والتبويبات اللاحقة يحاكي git diff -b ويُسكت ضوضاء CRLF. تجاهل الأسطر الفارغة يُسقط الأسطر الفارغة، فيتوقف إعادة تخطيط الفقرات عن إخراج المقارنة من المحاذاة. كل خيار مستقل ويُحفظ بين الجلسات.

ما هي صيغة unified diff؟

Unified diff هي صيغة ---/+++/@@ -L,C +L,C @@ النصية التي أدخلتها GNU diff في أواخر الثمانينات، ويستخدمها git وGitHub وGitLab وأمر patch في Unix. تُظهر كل كتلة ثلاثة أسطر سياق حول التغيير، مع - للمحذوف و+ للمُضاف. انسخ مخرجات unified إلى تعليق PR، أو الصقها في git apply، أو شغّل patch -p1 < diff.patch فتنطبق بسلاسة.

Myers مقابل Patience: أيّ خوارزمية مقارنة أفضل لمراجعة الكود؟

Myers هي الافتراضية في git diff وGNU diff: سريعة ومُصغَّرة رياضيًا، لكنها أحيانًا تحاذي أسطرًا فارغة أو أقواسًا مُغلِقة لا علاقة لها بالموضوع، فتُنتج مقارنات «تُقرأ بشكل غريب». تُرسي Patience (Bram Cohen، 2005) على الأسطر التي تظهر مرة واحدة بالضبط في كل مدخل وتستدعي نفسها بين المراسي، فتبقى حدود الدوال سليمة. استخدم git diff —patience (أو —histogram لنتائج مماثلة أسرع قليلًا) عند مراجعة إعادة الهيكلة.

هل يُرسَل النصّ الذي ألصقه إلى أيّ خادم؟

لا. تجري كل مقارنة في مقارنة النصوص محليًا بـJavaScript داخل متصفحك. لا يُرفَع نصّك أبدًا، ولا يُسجَّل، ولا يُخزَّن على القرص، ولا يُرسَل إلى أيّ طرف ثالث. تُحفظ فقط تفضيلات الواجهة (وضع العرض ومفاتيح التجاهل) في localStorage كي تتذكّرها الصفحة في الزيارة القادمة، دون النصّ. تحقّق عبر DevToolsNetwork: صفر طلب يُطلق عند نقر Diff.

ما حجم المدخلين الأقصى؟

الحدّ العملي نحو 5,000 سطر أو 1 ميغابايت لكل طرف. تتعطّل المقارنة الحيّة فوق 200 كيلوبايت مجتمعة وتنتقل إلى زرّ مقارنة يدويّ. فوق 5,000 سطر يُقطع المدخل مع تحذير. للملفات بحجم ميغابايتات، انتقل إلى diff -u a.txt b.txt أو git diff —no-index a.txt b.txt أو delta، فهي تُبَثّ وتتعامل مع الغيغابايتات.

مقالات ذات صلة

عرض جميع المقالات