ToolDeck

مقارنة النصوص

قارن بين نصّين جنبًا إلى جنب وأبرز الفروق سطرًا بسطر

جرب مثالاً

النص أ

النص ب

يعمل محليًا · آمن للصق الأسرار
يعمل محليًا · آمن للصق الأسرار

ما هي أداة مقارنة النصوص (Text Diff)؟

مقارنة النصوص (text diff، اختصار لـ"difference") هي نتيجة مقارنة مقطعَي نص وتحديد الأسطر التي أُضيفت أو حُذفت أو بقيت دون تغيير. يعود مفهوم diff إلى أداة Unix الشهيرة التي صدرت عام 1974 ضمن الإصدار الخامس من Unix. واليوم، تُشكّل مقارنة النصوص العمود الفقري لأنظمة التحكم في الإصدارات كـGit، إذ يخزّن كل commit فروقًا (diff) بدلًا من نسخة كاملة من كل ملف.

تعمل خوارزمية diff بإيجاد أطول تسلسل مشترك (LCS) بين سلسلتَي أسطر. الأسطر الموجودة في LCS تُصنَّف على أنها غير متغيرة. الأسطر الموجودة في النص الأصلي فقط خارج LCS تُصنَّف على أنها محذوفة. والأسطر الموجودة في النص المعدَّل فقط تُصنَّف على أنها مضافة. والنتيجة هي الحد الأدنى من التغييرات اللازمة لتحويل نص إلى الآخر.

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

لماذا تستخدم أداة مقارنة نصوص عبر الإنترنت؟

مقارنة النصوص في الطرفية تتطلب تثبيت أدوات diff والتعامل مع خيارات سطر الأوامر. أداة المقارنة المستندة إلى المتصفح تُغنيك عن كل هذا.

مقارنة فورية
الصق مقطعَي نص وشاهد الفروق مُبرَّزة على الفور. لا حاجة لإنشاء ملفات، ولا أوامر يجب تذكّرها، ولا إخراج يتطلب تحليلًا.
🔒
المعالجة تتم على جهازك
تتم جميع عمليات المقارنة في متصفحك باستخدام JavaScript. نصّك لا يغادر جهازك أبدًا، وهذا مهم عند مقارنة ملفات الإعداد أو بيانات الاعتماد أو الكود الخاص.
📋
إخراج جاهز للنسخ
يستخدم إخراج الفروق بادئات + / - المعيارية المتوافقة مع صيغة unified diff. يمكنك نسخ النتيجة مباشرةً إلى رسائل commit أو تعليقات مراجعة الكود أو تقارير الأخطاء.
🌐
بدون تسجيل أو تثبيت
تعمل على أي جهاز مزوّد بمتصفح. لا إنشاء حساب، ولا إضافات، ولا تطبيق سطح مكتب. افتح الصفحة وابدأ المقارنة.

حالات استخدام مقارنة النصوص

تطوير الواجهة الأمامية
قارن إخراج CSS أو HTML المضغوط قبل وبعد خطوة البناء لاكتشاف التغييرات غير المقصودة في الترميز أو الأنماط المُولَّدة.
هندسة الواجهة الخلفية
قارن استجابات API عبر البيئات المختلفة (التجريبية مقابل الإنتاجية) للتحقق من أن عملية النشر لم تُحدث تغييرات غير متوقعة في البيانات.
DevOps والبنية التحتية
قارن مشخّصات Kubernetes أو خطط Terraform أو إعدادات Nginx قبل تطبيق التغييرات على خادم أو مجموعة نشطة.
ضمان الجودة والاختبار
تحقق من أن إخراج الاختبار يطابق الخطوط الأساسية المتوقعة عبر مقارنة النتيجة الفعلية بملف لقطة مخزّن.
هندسة البيانات
قارن ترويسات CSV أو تعريفات مخطط SQL عبر عمليات ترحيل قاعدة البيانات للتأكد من إضافة الأعمدة أو إعادة تسميتها بشكل صحيح.
التعلم والدراسة
قارن حلّك بتطبيق مرجعي سطرًا بسطر لاكتشاف أين يختلف منطقك عن الأسلوب المتوقع.

مقارنة صيغ إخراج diff

تُنتج أدوات diff الإخراج في عدة صيغ. يلخّص الجدول أدناه أكثرها شيوعًا، وما الذي يُولّدها، ومتى تكون كل منها مفيدة.

الصيغةالأداة / المصدرالوصف
Unified diffdiff -u / git diffPrefixes lines with + / - / space; includes @@ hunk headers
Side-by-sidediff -y / sdiffTwo columns, changed lines aligned horizontally
Context diffdiff -cShows changed lines with surrounding context, marked with ! / + / -
HTML diffPython difflibColor-coded HTML table with inline change highlights
JSON PatchRFC 6902Array of add/remove/replace operations on a JSON document

كيف تعمل مقارنة الأسطر: خوارزمية LCS

تستخدم معظم أدوات مقارنة الأسطر، بما فيها هذه الأداة، خوارزمية أطول تسلسل مشترك (LCS). تجد LCS أكبر مجموعة من الأسطر التي تظهر في كلا النصين بنفس الترتيب النسبي، دون اشتراط أن تكون متجاورة. الأسطر غير الموجودة في LCS هي الفروق الفعلية.

تستخدم خوارزمية LCS القياسية البرمجة الديناميكية وتعمل بزمن O(m x n)، حيث m وn هما عدد أسطر النصين. بالنسبة للملفات الكبيرة، تُقلّل المتغيرات المحسّنة كخوارزمية Myers' diff (المستخدمة في Git) هذا إلى O(n + d^2) حيث d هو عدد الفروق، مما يجعلها سريعة عندما تكون معظم الأسطر مشتركة.

1. بناء جدول البرمجة الديناميكية
أنشئ مصفوفة (m+1) x (n+1). لكل زوج من الأسطر، خزّن طول أطول تسلسل مشترك رُصد حتى الآن. الأسطر المتساوية تمدّ قيمة القطر بمقدار 1.
2. التتبع العكسي
انتقل من الزاوية السفلى اليمنى للمصفوفة إلى (0,0). الحركات القطرية على الأسطر المتساوية تُنتج إدخالات "غير متغيرة". الحركات الأفقية أو الرأسية تُنتج إدخالات "مضافة" أو "محذوفة".
3. عرض الإخراج
حوّل كل إدخال إلى سطر عرض: الأسطر غير المتغيرة بدون بادئة، والمضافة تحصل على +، والمحذوفة تحصل على -. طبّق الترميز اللوني للوضوح البصري.

أمثلة على التعليمات البرمجية

تطبيقات للمقارنة النصية سطرًا بسطر في JavaScript وPython وGo وسطر الأوامر. كل مثال يُنتج إخراجًا بصيغة unified diff.

JavaScript
// Line-by-line diff using the LCS algorithm
function diffLines(a, b) {
  const linesA = a.split('\n')
  const linesB = b.split('\n')

  // Build LCS table
  const m = linesA.length, n = linesB.length
  const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0))
  for (let i = 1; i <= m; i++)
    for (let j = 1; j <= n; j++)
      dp[i][j] = linesA[i-1] === linesB[j-1]
        ? dp[i-1][j-1] + 1
        : Math.max(dp[i-1][j], dp[i][j-1])

  // Backtrack to produce diff
  const result = []
  let i = m, j = n
  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && linesA[i-1] === linesB[j-1]) {
      result.unshift({ type: 'equal', text: linesA[i-1] }); i--; j--
    } else if (j > 0 && (i === 0 || dp[i][j-1] >= dp[i-1][j])) {
      result.unshift({ type: 'add', text: linesB[j-1] }); j--
    } else {
      result.unshift({ type: 'remove', text: linesA[i-1] }); i--
    }
  }
  return result
}

const diff = diffLines("alpha\nbeta\ngamma", "alpha\nbeta changed\ngamma\ndelta")
// → [
//   { type: 'equal',  text: 'alpha' },
//   { type: 'remove', text: 'beta' },
//   { type: 'add',    text: 'beta changed' },
//   { type: 'equal',  text: 'gamma' },
//   { type: 'add',    text: 'delta' }
// ]
Python
import difflib

text_a = """alpha
beta
gamma""".splitlines()

text_b = """alpha
beta changed
gamma
delta""".splitlines()

# Unified diff (same format as git diff)
for line in difflib.unified_diff(text_a, text_b, fromfile='a.txt', tofile='b.txt', lineterm=''):
    print(line)
# --- a.txt
# +++ b.txt
# @@ -1,3 +1,4 @@
#  alpha
# -beta
# +beta changed
#  gamma
# +delta

# HTML side-by-side diff
d = difflib.HtmlDiff()
html = d.make_file(text_a, text_b, fromdesc='Original', todesc='Modified')
Go
package main

import (
	"fmt"
	"strings"
)

// Minimal LCS-based line diff
func diffLines(a, b string) {
	la := strings.Split(a, "\n")
	lb := strings.Split(b, "\n")
	m, n := len(la), len(lb)

	dp := make([][]int, m+1)
	for i := range dp {
		dp[i] = make([]int, n+1)
	}
	for i := 1; i <= m; i++ {
		for j := 1; j <= n; j++ {
			if la[i-1] == lb[j-1] {
				dp[i][j] = dp[i-1][j-1] + 1
			} else if dp[i-1][j] >= dp[i][j-1] {
				dp[i][j] = dp[i-1][j]
			} else {
				dp[i][j] = dp[i][j-1]
			}
		}
	}

	var result []string
	i, j := m, n
	for i > 0 || j > 0 {
		if i > 0 && j > 0 && la[i-1] == lb[j-1] {
			result = append([]string{" " + la[i-1]}, result...)
			i--; j--
		} else if j > 0 && (i == 0 || dp[i][j-1] >= dp[i-1][j]) {
			result = append([]string{"+" + lb[j-1]}, result...)
			j--
		} else {
			result = append([]string{"-" + la[i-1]}, result...)
			i--
		}
	}

	for _, line := range result {
		fmt.Println(line)
	}
}

// Output:
//  alpha
// -beta
// +beta changed
//  gamma
// +delta
CLI (diff / git)
# Compare two files with unified diff (3 lines of context)
diff -u original.txt modified.txt

# Git diff between working tree and last commit
git diff HEAD -- file.txt

# Git diff between two branches
git diff main..feature -- src/

# Side-by-side diff in the terminal
diff -y --width=120 original.txt modified.txt

# Color-coded diff (requires colordiff)
diff -u original.txt modified.txt | colordiff

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

ما الفرق بين مقارنة الأسطر ومقارنة الأحرف؟
تقارن مقارنة الأسطر النص سطرًا بسطر: إذا تغيّر أي حرف في سطر ما، يُصنَّف السطر بأكمله على أنه محذوف وتُصنَّف النسخة الجديدة على أنها مضافة. أما مقارنة الأحرف (أو الكلمات) فتعمل بدقة أكبر، وتُحدّد الأحرف أو الكلمات التي تغيّرت داخل السطر. مقارنة الأسطر هي المعيار في مراجعة الكود؛ أما مقارنة الأحرف فأكثر فائدة لتحرير النصوص.
كيف يعمل git diff داخليًا؟
يستخدم Git خوارزمية Myers' diff افتراضيًا، التي تجد أقصر مسار للتعديل (الحد الأدنى من الإضافات والحذف) بين ملفين. يخزّن Git النتيجة بصيغة unified diff مع ترويسات hunk بـ@@ تشير إلى أرقام الأسطر. عند تشغيل git diff، يقارن Git شجرة العمل بمنطقة الفهرس (staging area)، لا الـcommit الأخير، إلا إذا مرّرت HEAD صراحةً.
هل يمكنني مقارنة الملفات الثنائية بأداة مقارنة نصوص؟
لا. تقسّم أدوات مقارنة النصوص المدخلات عند أحرف السطر الجديد وتقارن السلاسل النصية. تحتوي الملفات الثنائية على تسلسلات بايت عشوائية تُنتج تقسيمات سطرية بلا معنى. للمقارنة الثنائية، استخدم أداة مقارنة hex أو مقارنة الملفات على مستوى المجموع التدقيقي (sha256sum على كلا الملفين).
هل تُرسَل بياناتي إلى خادم عند استخدام هذه الأداة؟
لا. تعمل هذه الأداة بالكامل في متصفحك. تستخدم عملية حساب الفروق تطبيق LCS بـJavaScript يُنفَّذ على جانب العميل. لا تُرسَل أي طلبات شبكة تحمل محتوى نصّك. يمكنك التحقق من ذلك بفتح علامة تبويب Network في متصفحك أثناء استخدام الأداة.
ما هي صيغة unified diff؟
unified diff هي صيغة الإخراج التي يستخدمها diff -u وgit diff. تعرض الأسطر المتغيرة مع بادئات - (محذوف) و+ (مضاف)، مسبوقةً بترويسات @@ تحدد نطاقات أرقام الأسطر في كلا الملفين. أسطر السياق (غير المتغيرة) لها بادئة مسافة. صيغة unified diff هي الأكثر شيوعًا للتصحيحات وطلبات السحب وأدوات مراجعة الكود.
كيف أقارن ملفات كبيرة تحتوي على فروق كثيرة؟
بالنسبة للملفات التي تحتوي على آلاف الأسطر، تعمل الأداة المستندة إلى المتصفح لكنها قد تبطئ لأن خوارزمية LCS تتوسع تربيعيًا. للفروق الكبيرة جدًا، أدوات سطر الأوامر كـdiff أو git diff أسرع لأنها تستخدم تطبيقات C محسّنة ويمكنها بث الإخراج. يمكنك أيضًا إخفاء الأسطر غير المتغيرة (تعطيل "عرض الأسطر غير المتغيرة" في هذه الأداة) للتركيز على التغييرات فقط.
ما هو three-way merge diff؟
يقارن three-way merge نسختين معدَّلتين من ملف بنسختهما الأصلية المشتركة (القاعدة). يستخدم Git هذا أثناء git merge وgit rebase. يُحدّد التغييرات التي أُجريت في كل فرع نسبةً إلى القاعدة ويدمجها. عندما يُعدّل كلا الفرعين السطر نفسه، يُبلّغ Git عن تعارض في الدمج. يتطلب three-way merge ثلاثة مدخلات، بينما يحتاج diff القياسي إلى مدخلَين فقط.