کتابخانه‌ی استاندارد · پایه

تفاوت text.diff

مقایسه‌ی خط‌به‌خط دو متن، چاپ تفاوت به شکل یکپارچه (unified diff) و سنجش شباهت.

واردسازی متن.تفاوت

نام آرگومان‌ها شناسه‌ی خود کد است و معادل فارسی ندارد؛ گونه‌ها، کلیدواژه‌ها و نام بسته‌ها با معادل فارسی نوشته می‌شوند.

بسته‌ی تفاوت پیدا می‌کند میان دو متن چه چیزی عوض شده است. دو متن را خط‌به‌خط مقایسه می‌کند، کوتاه‌ترین فهرست خط‌های حذف‌شده و افزوده را می‌یابد و می‌تواند نتیجه را به شکل تفاوت یکپارچه چاپ کند، همان قالب diff -u و git diff: دو سرخط --- و +++ و بعد تکه‌هایی که با @@ -1,3 +1,4 @@ شروع می‌شوند و خط‌ها را با ، - و + نشان می‌دهند. خروجی دقیقاً مثل difflib.unified_diff پایتون است، پس ابزاری که یکی را می‌خواند دیگری را هم می‌خواند. برای آزمونی که خروجی تولیدشده را با پرونده‌ی انتظار مقایسه می‌کند، برای مرحله‌ی ساختی که نشان می‌دهد یک تولیدکننده چه چیزی را عوض کرده و برای بررسی این‌که چیزی فرق کرده یا نه به کار می‌آید.

دو سطح دارد. تفاوت یکپارچه و تفاوت یکپارچه متن خط‌های آماده می‌دهند؛ دستورهای ویرایش خود فهرست ویرایش را می‌دهد، چند رکورد دستور ویرایش که می‌گویند «خط‌های a[i1:i2] با b[j1:j2] عوض شده‌اند»، برای برنامه‌ای که می‌خواهد تفاوت را به شکل خودش نشان دهد. نسبت شباهت و نسبت شباهت متن عددی میان ۰ و ۱ می‌دهند که می‌گوید دو ورودی چقدر شبیه‌اند.

اشتباه‌های رایج:

  • این‌که چیزی برنگشتن را خطا بدانید. وقتی دو ورودی برابرند، تفاوت یکپارچه فهرست خالی و تفاوت یکپارچه متن رشته‌ی خالی برمی‌گرداند، حتی بدون سرخط. همین راه آزمودن «تفاوتی نیست» است.
  • دادن متن به‌جای خط‌ها. تفاوت یکپارچه یک وکتور<رشته> می‌گیرد؛ متن را اول با خط ها بشکنید یا تفاوت یکپارچه متن را صدا بزنید. خط ها تکه‌ی خالی بعد از خط‌نوی آخر و \r انتهای هر خط را کنار می‌گذارد، پس پرونده‌ی ویندوزی با پرونده‌ی یونیکسی برابر درمی‌آید.
  • خواندن نسبت به‌عنوان فاصله‌ی ویرایش. نسبت شباهت برابر 2*M/T است که M تعداد خط‌های مشترک به همان ترتیب و T مجموع طول‌های دو ورودی است. از بلندترین زیردنباله‌ی مشترک ساخته می‌شود، پس ممکن است کمی بیشتر از difflib.SequenceMatcher.ratio() شود که روی بعضی ورودی‌ها زودتر می‌ایستد.
  • مقایسه‌ی ورودی‌های بسیار بزرگ و کاملاً متفاوت. جست‌وجو بعد از ۳۰۰۰ ویرایش می‌ایستد و باقی‌مانده را یک تکه‌ی جایگزین‌شده حساب می‌کند. نتیجه باز هم تفاوت درستی است، فقط کوتاه‌ترین نیست.
  • آزاد نکردن. خط ها، تفاوت یکپارچه، دستورهای ویرایش و دستورهای ویرایش گروه بندی شده وکتوری برمی‌گردانند که فراخواننده آزاد می‌کند (و هر وکتور درونی دستورهای ویرایش گروه بندی شده هم).

نشان دادن آنچه میان دو پرونده عوض شده

هر دو پرونده را بخوانید، با نام‌هایشان برای سرخط به تفاوت یکپارچه متن بدهید و اگر جواب خالی نبود چاپش کنید. آرگومان بعد از نام‌ها شمار خط‌های بدون تغییری است که دور هر تغییر نشان داده می‌شود (۳ انتخاب معمول است، ۰ فقط تغییرها را نشان می‌دهد).

text-diff-guide-files.salam
واردسازی متن.تفاوت
واردسازی سیستم پرونده.پرونده
واردسازی سیستم پرونده.مسیر
واردسازی سیستم عامل.موقت

تابع ریشه:
    work := موقت.پوشه موقت()
    دیرکن موقت.حذف پوشه(work)
    left := مسیر.پیوند(work، "config.old")
    right := مسیر.پیوند(work، "config.new")
    پرونده.نوشتن متن(left، "port=80\nhost=a\nmode=safe\n")
    پرونده.نوشتن متن(right، "port=8080\nhost=a\nmode=safe\ndebug=1\n")
    patch := تفاوت.تفاوت یکپارچه متن(پرونده.خواندن متن(left)، پرونده.خواندن متن(right)، "config.old"، "config.new"، 1)
    اگر patch.طول() برابر 0:
        سرچاپ "the files are the same"
    وگرنه:
        چاپ patch
    پایان
    سرچاپ "same file:"، تفاوت.تفاوت یکپارچه متن("x\n"، "x\n"، "a"، "b"، 3).طول() برابر 0
پایان
خروجی--- config.old +++ config.new @@ -1,3 +1,4 @@ -port=80 +port=8080 host=a mode=safe +debug=1 same file: true

پیشنهاد نزدیک‌ترین نام

نسبت شباهت متن «این دو واژه چقدر شبیه‌اند» را به یک عدد تبدیل می‌کند، پس برنامه می‌تواند به غلط تایپی با «منظورتان این بود؟» جواب بدهد. نمونه‌ی زیر همه‌ی گزینه‌ها را می‌سنجد، بالاترین امتیاز را نگه می‌دارد و فقط وقتی پیشنهاد می‌دهد که امتیاز دست‌کم ۰.۶ باشد. نسبت شباهت همین کار را برای فهرست خط‌ها می‌کند: سه خط از چهار خط مشترک است، پس امتیاز ۰.۷۵ می‌شود.

text-diff-guide-similar.salam
// زبان: فارسی
واردسازی متن.تفاوت

تابع نزدیک ترین(واژه: رشته، گزینه ها: رشته[]): رشته:
    ناپایا برگزیده := ""
    ناپایا بهترین امتیاز := 0.0
    هر گزینه در گزینه ها:
        امتیاز := تفاوت.نسبت شباهت متن(واژه، گزینه)
        اگر امتیاز بزرگتر بهترین امتیاز:
            بهترین امتیاز = امتیاز
            برگزیده = گزینه
        پایان
    پایان
    برگشت برگزیده
پایان

تابع ریشه:
    دستورها := ["start"، "stop"، "status"، "restart"، "version"]
    هر نوشته در ["starts"، "stops"، "restarts"، "xyz"]:
        حدس := نزدیک ترین(نوشته، دستورها)
        امتیاز := حدس.طول() بزرگتر 0 ? تفاوت.نسبت شباهت متن(نوشته، حدس) : 0.0
        اگر امتیاز بزرگتربرابر 0.6:
            سرچاپ نوشته، "->"، حدس
        وگرنه:
            سرچاپ نوشته، "-> no suggestion"
        پایان
    پایان

    قدیمی := تفاوت.خط ها("a\nb\nc\nd\n")
    تازه := تفاوت.خط ها("a\nb\nx\nd\n")
    دیرکن قدیمی.آزادکن()
    دیرکن تازه.آزادکن()
    سرچاپ "lines in common:"، تفاوت.نسبت شباهت(قدیمی، تازه)
پایان
خروجیstarts -> start stops -> stop restarts -> restart xyz -> no suggestion lines in common: 0.75

گونه‌ها

دستور ویرایش Opcode

ساختار دستور ویرایش
یک گام از فهرست ویرایش: چه بر سر a[a_start:a_stop] نسبت به b[b_start:b_stop] آمده است. برای حذف بازه‌ی ورودی دوم و برای افزودن بازه‌ی ورودی اول خالی است.
نامانگلیسیگونهتوضیح
نوعtagصحیح = 0یکی از همسان، جایگزین، حذف شده و افزوده.
آغاز الفa_startصحیح = 0شماره‌ی اولین خط از ورودی اول که این گام می‌پوشاند.
انتهای الفa_stopصحیح = 0شماره‌ی بعد از آخرین خط از ورودی اول که این گام می‌پوشاند.
آغاز بb_startصحیح = 0شماره‌ی اولین خط از ورودی دوم که این گام می‌پوشاند.
انتهای بb_stopصحیح = 0شماره‌ی بعد از آخرین خط از ورودی دوم که این گام می‌پوشاند.
text-diff-Opcodes.salam
واردسازی متن.تفاوت
واردسازی رشته

تابع name(tag: صحیح): رشته:
    ترابرد tag:
        تفاوت.همسان:
            برگشت "equal"
        پایان
        تفاوت.جایگزین:
            برگشت "replace"
        پایان
        تفاوت.حذف شده:
            برگشت "delete"
        پایان
        وگرنه:
            برگشت "insert"
        پایان
    پایان
پایان

تابع ریشه:
    a := تفاوت.خط ها("one\ntwo\nthree\nfour\n")
    b := تفاوت.خط ها("one\n2\nthree\nfour\nfive\n")
    دیرکن a.آزادکن()
    دیرکن b.آزادکن()
    ops := تفاوت.دستورهای ویرایش(a، b)
    دیرکن ops.آزادکن()
    هر op در ops:
        سرچاپ name(op.نوع)، "a[" + رشته.از عدد(op.آغاز الف برگردان صحیح۶۴) + ":" + رشته.از عدد(op.انتهای الف برگردان صحیح۶۴) + "]"، "b[" + رشته.از عدد(op.آغاز ب برگردان صحیح۶۴) + ":" + رشته.از عدد(op.انتهای ب برگردان صحیح۶۴) + "]"
    پایان
    groups := تفاوت.دستورهای ویرایش گروه بندی شده(a، b، 0)
    سرچاپ "hunks:"، groups.طول()
    هر g در groups:
        g.آزادکن()
    پایان
    groups.آزادکن()
پایان
خروجیequal a[0:1] b[0:1] replace a[1:2] b[1:2] equal a[2:4] b[2:4] insert a[4:4] b[4:5] hunks: 2

پایاها و متغیرها

همسان Equal

پایا همسان: صحیح۳۲ = 0
برچسب دستور برای خط‌هایی که در هر دو ورودی یکی هستند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Opcodes.salam

جایگزین Replace

پایا جایگزین: صحیح۳۲ = 1
برچسب دستور برای خط‌های ورودی اول که با خط‌های متفاوتی از ورودی دوم عوض شده‌اند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Opcodes.salam

حذف شده Delete

پایا حذف شده: صحیح۳۲ = 2
برچسب دستور برای خط‌هایی که فقط در ورودی اول هستند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Opcodes.salam

افزوده Insert

پایا افزوده: صحیح۳۲ = 3
برچسب دستور برای خط‌هایی که فقط در ورودی دوم هستند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Opcodes.salam

تابع‌ها

دستورهای ویرایش Opcodes

تابع دستورهای ویرایش(a: وکتور<رشته>، b: وکتور<رشته>): وکتور<دستور ویرایش>
فهرست ویرایشی که a را به b تبدیل می‌کند: دستورهایی پشت سر هم که با هم هر دو ورودی را از اول تا آخر می‌پوشانند. حذف و افزودن‌های کنار هم در یک جایگزین جمع می‌شوند. وکتور را فراخواننده آزاد می‌کند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Opcodes.salam

دستورهای ویرایش گروه بندی شده GroupedOpcodes

تابع دستورهای ویرایش گروه بندی شده(a: وکتور<رشته>، b: وکتور<رشته>، context: صحیح): وکتور<وکتور<دستور ویرایش>>
دستورها به تکه‌هایی بریده می‌شوند که کنار هر تغییر حداکثر context خط بدون تغییر دارند، و وقتی ورودی‌ها برابرند هیچ تکه‌ای نیست. مقدار منفی context مثل ۰ حساب می‌شود. وکتور بیرونی و تک‌تک وکتورهای درونی را فراخواننده آزاد می‌کند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Opcodes.salam

تفاوت یکپارچه Unified

تابع تفاوت یکپارچه(a: وکتور<رشته>، b: وکتور<رشته>، from_name: رشته، to_name: رشته، context: صحیح): وکتور<رشته>
تفاوت به شکل خط‌های یک تفاوت یکپارچه: سرخط‌های --- و +++ با دو نام، بعد هر تکه با خط @@ خودش. خط‌ها پایان‌دهنده‌ی خط ندارند. ورودی‌های برابر وکتور خالی می‌دهند. وکتور را فراخواننده آزاد می‌کند.
text-diff-Unified.salam
واردسازی متن.تفاوت

تابع ریشه:
    old := تفاوت.خط ها("red\ngreen\nblue\n")
    fresh := تفاوت.خط ها("red\nyellow\nblue\nblack\n")
    دیرکن old.آزادکن()
    دیرکن fresh.آزادکن()
    lines := تفاوت.تفاوت یکپارچه(old، fresh، "old.txt"، "new.txt"، 1)
    دیرکن lines.آزادکن()
    هر line در lines:
        سرچاپ line
    پایان
    چاپ تفاوت.تفاوت یکپارچه متن("a\nb\n"، "a\nc\n"، "x"، "y"، 0)
پایان
خروجی--- old.txt +++ new.txt @@ -1,3 +1,4 @@ red -green +yellow blue +black --- x +++ y @@ -2 +2 @@ -b +c

خط ها Lines

تابع خط ها(text: رشته): وکتور<رشته>
متن را به خط‌ها می‌شکند: روی \n، تکه‌ی خالی بعد از خط‌نوی آخر و \r انتهای خط را کنار می‌گذارد. متن خالی خطی ندارد. وکتور را فراخواننده آزاد می‌کند.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Unified.salam

تفاوت یکپارچه متن UnifiedText

تابع تفاوت یکپارچه متن(a: رشته، b: رشته، from_name: رشته، to_name: رشته، context: صحیح): رشته
همان تفاوت یکپارچه برای دو متن، به‌صورت یک رشته با خط‌نو بعد از هر خط؛ وقتی دو متن خط‌های یکسان دارند خالی است.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Unified.salam

نسبت شباهت Ratio

تابع نسبت شباهت(a: وکتور<رشته>، b: وکتور<رشته>): اعشار۶۴
دو فهرست خط چقدر شبیه‌اند، از ۰ (هیچ چیز مشترک نیست) تا ۱ (یکی‌اند): دو برابر شمار خط‌های مشترک به همان ترتیب، تقسیم بر مجموع طول‌ها. دو فهرست خالی ۱ می‌دهد.
text-diff-Ratio.salam
واردسازی متن.تفاوت

تابع ریشه:
    سرچاپ تفاوت.نسبت شباهت متن("kitten"، "sitting")
    سرچاپ تفاوت.نسبت شباهت متن("same"، "same")، تفاوت.نسبت شباهت متن("abc"، "xyz")
    a := تفاوت.خط ها("a\nb\nc\nd\n")
    b := تفاوت.خط ها("a\nb\nx\nd\n")
    دیرکن a.آزادکن()
    دیرکن b.آزادکن()
    سرچاپ تفاوت.نسبت شباهت(a، b)
پایان
خروجی0.6153846153846154 1 0 0.75

نسبت شباهت متن RatioText

تابع نسبت شباهت متن(a: رشته، b: رشته): اعشار۶۴
همین سنجش برای دو رشته که با نویسه شمرده می‌شود (نقطه‌ی کد، پس متن فارسی با حرف شمرده می‌شود). برای تطبیق تقریبی نام‌ها و فرمان‌ها.

نمونه‌ی این مورد همراه با موارد بالاتر آمده است: text-diff-Ratio.salam