GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||
GeezerVison is showing python.github.io — difflib --- کمککنندههایی برای محاسبهی دلتاها
|
کد |
معنی |
|---|---|
|
خط مختص دنبالهی ۱ |
|
خط یکتا برای دنباله ۲ |
|
خط مشترک بین هر دو دنباله |
|
خط در هیچیک از دنبالههای ورودی وجود ندارد |
سطرهایی که با '?' شروع میشوند، تلاش میکنند توجه را به تفاوتهای درونخطی جلب کنند، و در هیچیک از دنبالههای ورودی وجود نداشتند. این سطرهای ممکن است در صورتی گیجکننده باشند که دنبالهها حاوی نویسههای فضای خالی مانند فاصلهها، تبها یا سطرهای جدید باشند.
توجه داشته باشید که دلتاهای تولیدشده توسط Differ ادعایی ندارند که تفاوتهای کمینه باشند. برعکس، تفاوتهای کمینه اغلب برای انسانها برخلاف شهود هستند، زیرا در هر جایی که ممکن باشد همگام میشوند، گاهی در تطابقهای تصادفی که ۱۰۰ صفحه با یکدیگر فاصله دارند. محدود کردن نقاط همگامسازی به تطابقهای متوالی، تا حدی مفهوم محلی بودن را حفظ میکند، هرچند گاهی به بهای تولید یک تفاوت طولانیتر تمام میشود.
کلاس Differ این سازنده را دارد:
پارامترهای کلیدواژهای اختیاری linejunk و charjunk برای توابع فیلتر (یا None) هستند:
linejunk: تابعی که تنها یک آرگومان رشتهای میپذیرد و اگر رشته هرز باشد، مقدار true را برمیگرداند. مقدار پیشفرض None است، به این معنا که هیچ خطی هرز در نظر گرفته نمیشود.
charjunk: تابعی که یک آرگومان تکنویسهای (رشتهای به طول ۱) میپذیرد و اگر نویسه هرز باشد، مقدار درست برمیگرداند. مقدار پیشفرض None است، یعنی هیچ نویسهای هرز در نظر گرفته نمیشود.
این توابع پالایش زباله (junk-filtering) سرعت تطبیق برای یافتن تفاوتها را افزایش میدهند و باعث نمیشوند هیچ خط یا نویسهی متفاوتی نادیده گرفته شود. برای توضیح، شرح پارامتر isjunk متد find_longest_match() را بخوانید.
از اشیای Differ از طریق یک متد واحد استفاده میشود (دلتاها تولید میشوند):
دو دنباله از سطرها را مقایسه میکند و دلتا (دنبالهای از سطرهای) را تولید میکند.
هر دنباله باید شامل رشتههای تکسطری جداگانهای باشد که به خط جدید ختم میشوند. چنین دنبالههایی را میتوان از متد readlines() اشیاء شبهپرونده بهدست آورد. دلتای تولیدشده نیز از رشتههای ختمشده به خط جدید تشکیل شده است، که آمادهاند بههمینصورت از طریق متد writelines() یک شیء شبهپرونده چاپ شوند.
از این کلاس میتوان برای ایجاد یک جدول HTML (یا یک پرونده HTML کامل شامل جدول) استفاده کرد که مقایسهی کنار هم و خطبهخط متن را با برجستهسازی تغییرات بینخطی و درونخطی نشان میدهد. این جدول را میتوان در حالت تفاوت کامل یا زمینهای تولید کرد.
هشدار
سطرهای جدید پایانی پیش از diff حذف میشوند، بنابراین نتیجه ممکن است ناقص باشد. برای جزئیات gh-71896 را ببینید.
سازندهی این کلاس عبارت است از:
نمونهای از HtmlDiff را مقداردهی اولیه میکند.
tabsize یک آرگومان کلیدواژهای اختیاری برای تعیین فاصلهگذاری تب است و مقدار پیشفرض آن 8 است.
wrapcolumn یک کلیدواژه اختیاری برای مشخص کردن شماره ستونی است که سطرهای در آن شکسته و به خط بعد منتقل میشوند، و بهطور پیشفرض None است که در این حالت سطرها به خط بعد منتقل نمیشوند.
linejunk و charjunk آرگومانهای کلیدواژهای اختیاری هستند که به ndiff() ارسال میشوند (توسط HtmlDiff برای تولید تفاوتهای HTML بهصورت کنار هم استفاده میشود). برای مقادیر پیشفرض و توضیحات آرگومانها، مستندات ndiff() را ببینید.
متدهای زیر عمومی هستند:
fromlines و tolines (فهرستهایی از رشتهها) را مقایسه میکند و رشتهای را برمیگرداند که یک پرونده HTML کامل حاوی جدولی است که تفاوتها را بهصورت خطبهخط نشان میدهد و تغییرات بینخطی و درونخطی در آن برجسته شدهاند.
fromdesc و todesc آرگومانهای کلیدواژهای اختیاری هستند که برای مشخص کردن رشتههای سرآیند ستون پرونده مبدأ/مقصد استفاده میشوند (مقدار پیشفرض هر دو یک رشته خالی است).
context و numlines هر دو آرگومان کلیدواژهای اختیاری هستند. هنگامی که باید تفاوتهای همراه با زمینه نمایش داده شوند، context را روی True تنظیم کنید، در غیر این صورت مقدار پیشفرض False است تا پروندههای کامل نمایش داده شوند. مقدار پیشفرض numlines برابر 5 است. وقتی context برابر True است، numlines تعداد سطرهای زمینهای را که تفاوتهای برجستهشده را در بر میگیرند، کنترل میکند. وقتی context برابر False است، numlines تعداد سطرهایی را که هنگام استفاده از ابرپیوندهای «next» پیش از یک تفاوت برجستهشده نمایش داده میشوند، کنترل میکند (تنظیم آن روی صفر باعث میشود ابرپیوندهای «next» تفاوت برجستهشده بعدی را بدون هیچ زمینه پیشین در بالای مرورگر قرار دهند).
نکته
fromdesc و todesc بهعنوان HTML خنثینشده تفسیر میشوند و باید هنگام دریافت ورودی از منابع نامطمئن، بهدرستی خنثی شوند.
تغییر یافته در نسخهی 3.5: آرگومان فقط کلیدواژهای charset اضافه شد. مجموعهنویسه پیشفرض سند HTML از 'ISO-8859-1' به 'utf-8' تغییر کرد.
fromlines و tolines (فهرستهایی از رشتهها) را مقایسه میکند و رشتهای را برمیگرداند که یک جدول HTML کامل است و تفاوتهای خطبهخط را با تغییرات بینخطی و درونخطی برجستهشده نشان میدهد.
آرگومانهای این متد همان آرگومانهای متد make_file() هستند.
a و b (فهرستهایی از رشتهها) را مقایسه میکند؛ دلتایی (یک تولیدگر که سطرهای دلتا را تولید میکند) را در قالب context diff برمیگرداند.
دیفهای زمینهای (context diffs) روشی فشرده برای نمایش تنها سطرهایی هستند که تغییر کردهاند، بهعلاوه چند خط زمینه. تغییرها به سبک پیش/پس نمایش داده میشوند. تعداد سطرهای زمینه توسط n تنظیم میشود که مقدار پیشفرض آن ۳ است.
بهطور پیشفرض، سطرهای کنترلی تفاوت (diff) (آنهایی که *** یا --- دارند) با یک نویسهی خط جدید در انتها ایجاد میشوند. این موضوع مفید است تا ورودیهای ایجادشده از io.IOBase.readlines() به تفاوتهایی منجر شوند که برای استفاده با io.IOBase.writelines() مناسب باشند، زیرا هم ورودیها و هم خروجیها دارای نویسهی خط جدید در انتها هستند.
برای ورودیهایی که خط جدید پایانی ندارند، آرگومان lineterm را روی "" تنظیم کنید تا خروجی بهطور یکنواخت فاقد خط جدید باشد.
قالب diff زمینهای (context diff) معمولاً دارای سرآیندی برای نام پروندهها و زمانهای تغییر است. هر یک یا همهی این موارد را میتوان با استفاده از رشتههایی برای fromfile، tofile، fromfiledate و tofiledate مشخص کرد. زمانهای تغییر معمولاً در قالب ISO 8601 بیان میشوند. اگر مشخص نشوند، رشتهها بهطور پیشفرض خالی خواهند بود.
>>> import sys >>> from difflib import * >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] >>> sys.stdout.writelines(context_diff(s1, s2, fromfile='before.py', ... tofile='after.py')) *** before.py --- after.py *************** *** 1,4 **** ! bacon ! eggs ! ham guido --- 1,4 ---- ! python ! eggy ! hamster guido
برای مثالی با جزئیات بیشتر، یک رابط خط فرمان برای difflib را ببینید.
فهرستی از بهترین تطابقهای «بهاندازهی کافی خوب» را برمیگرداند. word دنبالهای است که تطابقهای نزدیک برای آن مطلوب است (معمولاً یک رشته)، و possibilities فهرستی از دنبالههاست که word با آنها تطبیق داده میشود (معمولاً فهرستی از رشتهها).
آرگومان اختیاری n (پیشفرض 3) حداکثر تعداد تطابقهای نزدیکی است که بازگردانده میشود؛ n باید بزرگتر از 0 باشد.
آرگومان اختیاری cutoff (پیشفرض 0.6) یک عدد اعشاری در بازه [۰، ۱] است. گزینههایی که امتیاز شباهت آنها به word حداقل این میزان نباشد، نادیده گرفته میشوند.
بهترین تطابقها (حداکثر n مورد) از میان گزینههای ممکن در یک فهرست بازگردانده میشوند و بر اساس امتیاز شباهت مرتب شدهاند، به طوری که مشابهترین مورد در ابتدا قرار دارد.
>>> get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy']) ['apple', 'ape'] >>> import keyword >>> get_close_matches('wheel', keyword.kwlist) ['while'] >>> get_close_matches('pineapple', keyword.kwlist) [] >>> get_close_matches('accept', keyword.kwlist) ['except']
a و b (فهرستهایی از رشتهها) را مقایسه میکند؛ یک دلتا به سبک Differ را برمیگرداند (یک تولیدگر که سطرهای دلتا را تولید میکند).
پارامترهای کلیدواژهای اختیاری linejunk و charjunk توابع پالایشگر (یا None) هستند:
linejunk: تابعی که تنها یک آرگومان رشتهای را میپذیرد، و اگر رشته هرز باشد مقدار درست، و در غیر این صورت مقدار نادرست را برمیگرداند. مقدار پیشفرض None است. همچنین یک تابع در سطح ماژول IS_LINE_JUNK() وجود دارد که سطرهایی را که نویسههای قابل مشاهده ندارند، بهجز حداکثر یک نویسه هش ('#')، فیلتر میکند -- با این حال، کلاس زیربنایی SequenceMatcher تحلیلی پویا انجام میدهد تا مشخص کند کدام سطرهای آنقدر پرتکرار هستند که نویز محسوب میشوند، و این معمولاً بهتر از استفاده از این تابع عمل میکند.
charjunk: تابعی که یک نویسه (رشتهای به طول ۱) میپذیرد، و اگر نویسه هرز (junk) باشد، مقداری برمیگرداند، و در غیر این صورت false برمیگرداند. مقدار پیشفرض، تابع IS_CHARACTER_JUNK() در سطح ماژول است که نویسههای فضای سفید (فاصله یا تب؛ کار بدی است که خط جدید را در آن بگنجانید!) را فیلتر میکند.
>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True), ... 'ore\ntree\nemu\n'.splitlines(keepends=True)) >>> print(''.join(diff), end="") - one ? ^ + ore ? ^ - two - three ? - + tree + emu
یکی از دو دنبالهای را که دلتایی (delta) تولید کردهاند، برمیگرداند.
با داشتن یک دنباله تولیدشده توسط Differ.compare() یا ndiff()، سطرهایی را که از پرونده ۱ یا ۲ آمدهاند (پارامتر which) استخراج کنید و پیشوندهای سطرها را حذف کنید.
مثال:
>>> diff = ndiff('one\ntwo\nthree\n'.splitlines(keepends=True), ... 'ore\ntree\nemu\n'.splitlines(keepends=True)) >>> diff = list(diff) # materialize the generated delta into a list >>> print(''.join(restore(diff, 1)), end="") one two three >>> print(''.join(restore(diff, 2)), end="") ore tree emu
a و b (فهرستهایی از رشتهها) را مقایسه میکند؛ یک دلتا (یک تولیدگر که سطرهای دلتا را تولید میکند) را در قالب unified diff بازمیگرداند.
تفاوتهای یکپارچه (unified diffs) روشی فشرده برای نمایش تنها سطرهایی که تغییر کردهاند بههمراه چند خط زمینه هستند. تغییرات بهصورت درونخطی نمایش داده میشوند (بهجای بلوکهای جداگانهی قبل/بعد). تعداد سطرهای زمینه با n تنظیم میشود که مقدار پیشفرض آن ۳ است.
بهطور پیشفرض، سطرهای کنترلی diff (آنهایی که دارای ---، +++ یا @@ هستند) با یک خط جدید در پایان ایجاد میشوند. این موضوع مفید است تا ورودیهایی که از io.IOBase.readlines() ایجاد شدهاند، منجر به diffهایی شوند که برای استفاده با io.IOBase.writelines() مناسب هستند، زیرا هم ورودیها و هم خروجیها دارای خط جدید در پایان هستند.
برای ورودیهایی که خط جدید پایانی ندارند، آرگومان lineterm را روی "" تنظیم کنید تا خروجی بهطور یکنواخت فاقد خط جدید باشد.
برای فعالسازی خروجی رنگی، مشابه git diff --color، مقدار color را روی True تنظیم کنید. حتی در صورت فعال بودن، میتوانید آن را با استفاده از متغیرهای محیطی کنترل کنید.
قالب unified diff معمولاً دارای سرآیندی برای نام پروندهها و زمانهای تغییر است. هرکدام یا همهی این موارد را میتوان با استفاده از رشتههایی برای fromfile، tofile، fromfiledate و tofiledate مشخص کرد. زمانهای تغییر معمولاً در قالب ISO 8601 بیان میشوند. اگر مشخص نشوند، رشتهها بهطور پیشفرض خالی خواهند بود.
>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n'] >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n'] >>> sys.stdout.writelines(unified_diff(s1, s2, fromfile='before.py', tofile='after.py')) --- before.py +++ after.py @@ -1,4 +1,4 @@ -bacon -eggs -ham +python +eggy +hamster guido
برای مثالی با جزئیات بیشتر، یک رابط خط فرمان برای difflib را ببینید.
تغییر یافته در نسخهی 3.15: پارامتر color اضافه شد.
a و b (فهرستهایی از اشیای bytes) را با استفاده از dfunc مقایسه میکند؛ دنبالهای از سطرهای دلتا (که آنها نیز bytes هستند) را در قالبی که dfunc برمیگرداند تولید میکند. dfunc باید یک شیء فراخوانیپذیر باشد، معمولاً یا unified_diff() یا context_diff().
به شما امکان میدهد دادههایی با کدگذاری ناشناخته یا ناهماهنگ را مقایسه کنید. همهی ورودیها بهجز n باید اشیای bytes باشند، نه str. با تبدیل بدون اتلاف همهی ورودیها (بهجز n) به str و فراخوانی dfunc(a, b, fromfile, tofile, fromfiledate, tofiledate, n, lineterm) کار میکند. سپس خروجی dfunc دوباره به bytes تبدیل میشود، بنابراین سطرهای دلتایی که دریافت میکنید، همان کدگذاریهای ناشناخته/ناهماهنگ a و b را دارند.
اضافه شده در نسخهی 3.5.
آرگومان اختیاری isjunk باید None (پیشفرض) یا تابعی با یک آرگومان باشد که عنصری از یک دنباله را میگیرد و اگر و تنها اگر آن عنصر «زائد» باشد و باید نادیده گرفته شود، مقدار درست برمیگرداند. پاس دادن None برای isjunk معادل پاس دادن lambda x: False است؛ به عبارت دیگر، هیچ عنصری نادیده گرفته نمیشود. برای مثال، این را پاس دهید:
lambda x: x in " \t"
اگر سطرها را بهعنوان دنبالههایی از نویسهها مقایسه میکنید و نمیخواهید بر اساس فاصلهها یا تبهای سخت همگامسازی کنید.
آرگومانهای اختیاری a و b دنبالههایی هستند که با هم مقایسه میشوند؛ مقدار پیشفرض هر دو، رشتههای خالی است. عناصر هر دو دنباله باید hashable باشند.
میتوانید از آرگومان اختیاری autojunk برای غیرفعال کردن هیوریستیک خودکار هرز استفاده کنید.
تغییر یافته در نسخهی 3.2: پارامتر autojunk اضافه شد.
اشیای SequenceMatcher سه ویژگی داده دارند: bjunk مجموعهای از عناصر b است که isjunk برای آنها True است؛ bpopular مجموعهای از عناصر غیرزائد است که توسط اکتشافی، محبوب در نظر گرفته میشوند (اگر غیرفعال نشده باشد)؛ b2j دیکشنری است که عناصر باقیماندهی b را به فهرستی از موقعیتهایی که در آنها ظاهر میشوند نگاشت میکند. هر سه هر زمان که b با set_seqs() یا set_seq2() بازنشانی شود، بازنشانی میشوند.
اضافه شده در نسخهی 3.2: ویژگیهای bjunk و bpopular.
اشیای SequenceMatcher متدهای زیر را دارند:
دو دنباله را برای مقایسه تنظیم میکند.
SequenceMatcher جزئیات مربوط به دنباله دوم را محاسبه و در نهانگاه ذخیره میکند، بنابراین اگر میخواهید یک دنباله را با چندین دنباله مقایسه کنید، از set_seq2() استفاده کنید تا دنبالهی پرکاربرد را یک بار تنظیم کنید و set_seq1() را بهطور مکرر، یک بار برای هر یک از دنبالههای دیگر، فراخوانی کنید.
نخستین دنبالهی مورد مقایسه را تنظیم کنید. دومین دنبالهی مورد مقایسه تغییر نمیکند.
دومین دنبالهی مورد مقایسه را تنظیم کنید. اولین دنبالهی مورد مقایسه تغییر نمیکند.
طولانیترین بلوک منطبق را در a[alo:ahi] و b[blo:bhi] بیابید.
اگر isjunk حذف شده باشد یا None باشد، find_longest_match() (i, j, k) را برمیگرداند بهطوری که a[i:i+k] برابر با b[j:j+k] است، که در آن alo <= i <= i+k <= ahi و blo <= j <= j+k <= bhi. برای همهی (i', j', k') که این شرایط را دارند، شرایط اضافی k >= k'، i <= i' و اگر i == i'، j <= j' نیز برقرار هستند. به عبارت دیگر، از میان همهی بلوکهای منطبق بیشینه، یکی را برمیگرداند که زودترین شروع را در a دارد، و از میان همهی آن بلوکهای منطبق بیشینه که زودترین شروع را در a دارند، آن را برمیگرداند که زودترین شروع را در b دارد.
>>> s = SequenceMatcher(None, " abcd", "abcd abcd") >>> s.find_longest_match(0, 5, 0, 9) Match(a=0, b=4, size=5)
اگر isjunk ارائه شده باشد، ابتدا طولانیترین بلوک منطبق همانگونه که در بالا توضیح داده شد تعیین میشود، اما با این محدودیت اضافی که هیچ عنصر هرز (junk) در بلوک وجود نداشته باشد. سپس آن بلوک تا حد ممکن با تطبیق (فقط) عناصر هرز در هر دو طرف گسترش مییابد. بنابراین بلوک حاصل هرگز بر عناصر هرز منطبق نمیشود، مگر آنکه عناصر هرز یکسان بهطور تصادفی مجاور یک تطبیق معنادار باشند.
این همان مثال پیشین است، اما فاصلهها را هرز (junk) در نظر میگیریم. این امر مانع از آن میشود که ' abcd' مستقیماً با ' abcd' در انتهای دنباله دوم تطابق داشته باشد. در عوض، تنها 'abcd' میتواند تطابق داشته باشد و با چپترین 'abcd' در دنباله دوم مطابقت دارد:
>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd") >>> s.find_longest_match(0, 5, 0, 9) Match(a=1, b=0, size=4)
اگر هیچ بلوکی مطابقت نداشته باشد، (alo, blo, 0) برگردانده میشود.
این متد یک named tuple با قالب Match(a, b, size) برمیگرداند.
تغییر یافته در نسخهی 3.9: آرگومانهای پیشفرض اضافه شد.
فهرستی از سهتاییها را برمیگرداند که زیردنبالههای منطبق غیرهمپوشان را توصیف میکنند. هر سهتایی به شکل (i, j, n) است و به این معناست که a[i:i+n] == b[j:j+n]. سهتاییها از نظر i و j بهطور یکنواخت صعودی هستند.
آخرین سهتایی ساختگی است و مقدار (len(a), len(b), 0) را دارد. این تنها سهتایی با n == 0 است. اگر (i, j, n) و (i', j', n') سهتاییهای مجاور در فهرست باشند و دومی آخرین سهتایی در فهرست نباشد، آنگاه i+n < i' یا j+n < j' برقرار است؛ به عبارت دیگر، سهتاییهای مجاور همیشه بلوکهای برابرِ غیرمجاور را توصیف میکنند.
>>> s = SequenceMatcher(None, "abxcd", "abcd") >>> s.get_matching_blocks() [Match(a=0, b=0, size=2), Match(a=3, b=2, size=2), Match(a=5, b=4, size=0)]
فهرستی از ۵-تاییها برمیگرداند که چگونگی تبدیل a به b را توصیف میکنند. هر ۵-تایی به شکل (tag, i1, i2, j1, j2) است. در اولین ۵-تایی، i1 == j1 == 0 است، و در ۵-تاییهای باقیمانده، i1 برابر با i2 از ۵-تایی پیشین است، و به همین ترتیب، j1 برابر با j2 پیشین است.
مقادیر tag رشتههایی با این معانی هستند:
مقدار |
معنی |
|---|---|
|
|
|
|
|
|
|
|
برای مثال:
>>> a = "qabxcd" >>> b = "abycdf" >>> s = SequenceMatcher(None, a, b) >>> for tag, i1, i2, j1, j2 in s.get_opcodes(): ... print('{:7} a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format( ... tag, i1, i2, j1, j2, a[i1:i2], b[j1:j2])) delete a[0:1] --> b[0:0] 'q' --> '' equal a[1:3] --> b[0:2] 'ab' --> 'ab' replace a[3:4] --> b[2:3] 'x' --> 'y' equal a[4:6] --> b[3:5] 'cd' --> 'cd' insert a[6:6] --> b[5:6] '' --> 'f'
یک تولیدگر از گروههایی با حداکثر n خط از زمینه برمیگرداند.
این متد با شروع از گروههای برگرداندهشده توسط get_opcodes()، خوشههای تغییر کوچکتر را جدا میکند و بازههای میانی را که تغییری ندارند حذف میکند.
گروهها در همان قالب get_opcodes() برگردانده میشوند.
معیاری از شباهت دنبالهها را بهصورت یک عدد اعشاری در بازهی [۰، ۱] برمیگرداند.
اگر T تعداد کل عناصر هر دو دنباله و M تعداد تطابقها باشد، این مقدار برابر با 2.0*M / T است. توجه داشته باشید که اگر دنبالهها یکسان باشند، این مقدار 1.0 است و اگر هیچ اشتراکی نداشته باشند، 0.0 است.
محاسبهی این مقدار در صورتی پرهزینه است که get_matching_blocks() یا get_opcodes() از قبل فراخوانی نشده باشند؛ در این صورت ممکن است بخواهید ابتدا quick_ratio() یا real_quick_ratio() را امتحان کنید تا کران بالایی به دست آورید.
سه متدی که نسبت نویسههای منطبق به کل نویسهها را برمیگردانند، میتوانند به دلیل سطوح متفاوت تقریب، نتایج متفاوتی داشته باشند، اگرچه quick_ratio() و real_quick_ratio() همواره دستکم به بزرگی ratio() هستند:
>>> s = SequenceMatcher(None, "abcd", "bcde") >>> s.ratio() 0.75 >>> s.quick_ratio() 0.75 >>> s.real_quick_ratio() 1.0
این مثال دو رشته را با در نظر گرفتن فاصلهها بهعنوان «زائد» مقایسه میکند:
>>> s = SequenceMatcher(lambda x: x == " ", ... "private Thread currentThread;", ... "private volatile Thread currentThread;")
ratio() یک عدد اعشاری در بازهی [۰، ۱] برمیگرداند که شباهت دنبالهها را میسنجد. بهعنوان یک قاعده سرانگشتی، مقدار ratio() بیش از ۰٫۶ به این معناست که دنبالهها تطابقهای نزدیکی هستند:
>>> print(round(s.ratio(), 3)) 0.866
اگر فقط به محل تطابق دنبالهها علاقهمند هستید، get_matching_blocks() مفید است:
>>> for block in s.get_matching_blocks(): ... print("a[%d] and b[%d] match for %d elements" % block) a[0] and b[0] match for 8 elements a[8] and b[17] match for 21 elements a[29] and b[38] match for 0 elements
توجه داشته باشید که آخرین چندگانهای که get_matching_blocks() برمیگرداند همیشه یک چندگانه ساختگی، (len(a), len(b), 0)، است و این تنها حالتی است که آخرین عنصر چندگانه (تعداد عناصر مطابقتدادهشده) برابر 0 است.
اگر میخواهید بدانید چگونه دنباله اول را به دنباله دوم تبدیل کنید، از get_opcodes() استفاده کنید:
>>> for opcode in s.get_opcodes(): ... print("%6s a[%d:%d] b[%d:%d]" % opcode) equal a[0:8] b[0:8] insert a[8:8] b[8:17] equal a[8:29] b[17:38]
همچنین ببینید
تابع get_close_matches() در این ماژول که نشان میدهد چگونه کد سادهای که بر پایهی SequenceMatcher ساخته شده است، میتواند برای انجام کار مفید استفاده شود.
دستور سادهی کنترل نسخه برای یک برنامهی کوچک ساختهشده با SequenceMatcher.
این مثال دو متن را مقایسه میکند. ابتدا متنها را آماده میکنیم، دنبالههایی از رشتههای تکسطری جداگانه که با خط جدید پایان مییابند (چنین دنبالههایی را همچنین میتوان از متد readlines() اشیای شبهپرونده به دست آورد):
>>> text1 = ''' 1. Beautiful is better than ugly. ... 2. Explicit is better than implicit. ... 3. Simple is better than complex. ... 4. Complex is better than complicated. ... '''.splitlines(keepends=True) >>> len(text1) 4 >>> text1[0][-1] '\n' >>> text2 = ''' 1. Beautiful is better than ugly. ... 3. Simple is better than complex. ... 4. Complicated is better than complex. ... 5. Flat is better than nested. ... '''.splitlines(keepends=True)
سپس یک شیء Differ را نمونهسازی میکنیم:
>>> d = Differ()
توجه داشته باشید که هنگام نمونهسازی یک شیء Differ، میتوانید توابعی را برای فیلتر کردن «اضافات» خطی و نویسهای ارسال کنید. برای جزئیات، سازندهی Differ() را ببینید.
در نهایت، این دو را با هم مقایسه میکنیم:
>>> result = list(d.compare(text1, text2))
result یک فهرست از رشتهها است، بنابراین بیایید آن را زیبانویسی کنیم (pretty-print):
>>> from pprint import pprint >>> pprint(result) [' 1. Beautiful is better than ugly.\n', '- 2. Explicit is better than implicit.\n', '- 3. Simple is better than complex.\n', '+ 3. Simple is better than complex.\n', '? ++\n', '- 4. Complex is better than complicated.\n', '? ^ ---- ^\n', '+ 4. Complicated is better than complex.\n', '? ++++ ^ ^\n', '+ 5. Flat is better than nested.\n']
بهصورت یک رشتهی چندخطی واحد، به این شکل است:
>>> import sys >>> sys.stdout.writelines(result) 1. Beautiful is better than ugly. - 2. Explicit is better than implicit. - 3. Simple is better than complex. + 3. Simple is better than complex. ? ++ - 4. Complex is better than complicated. ? ^ ---- ^ + 4. Complicated is better than complex. ? ++++ ^ ^ + 5. Flat is better than nested.
این مثال نشان میدهد که چگونه از difflib برای ایجاد یک ابزار مشابه diff استفاده کنید.
""" Command-line interface to difflib.py providing diffs in four formats: * ndiff: lists every line and highlights interline changes. * context: highlights clusters of changes in a before/after format. * unified: highlights clusters of changes in an inline format. * html: generates side by side comparison with change highlights. """ import sys, os, difflib, argparse import datetime as dt def file_mtime(path): t = dt.datetime.fromtimestamp(os.stat(path).st_mtime, dt.timezone.utc) return t.astimezone().isoformat() def main(): parser = argparse.ArgumentParser() parser.add_argument('-c', action='store_true', default=False, help='Produce a context format diff (default)') parser.add_argument('-u', action='store_true', default=False, help='Produce a unified format diff') parser.add_argument('-m', action='store_true', default=False, help='Produce HTML side by side diff ' '(can use -c and -l in conjunction)') parser.add_argument('-n', action='store_true', default=False, help='Produce a ndiff format diff') parser.add_argument('-l', '--lines', type=int, default=3, help='Set number of context lines (default 3)') parser.add_argument('fromfile') parser.add_argument('tofile') options = parser.parse_args() n = options.lines fromfile = options.fromfile tofile = options.tofile fromdate = file_mtime(fromfile) todate = file_mtime(tofile) with open(fromfile) as ff: fromlines = ff.readlines() with open(tofile) as tf: tolines = tf.readlines() if options.u: diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n) elif options.n: diff = difflib.ndiff(fromlines, tolines) elif options.m: diff = difflib.HtmlDiff().make_file(fromlines,tolines,fromfile,tofile,context=options.c,numlines=n) else: diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n) sys.stdout.writelines(diff) if __name__ == '__main__': main()
این مثال نشان میدهد که چگونه از difflib.ndiff() استفاده کنید.
"""ndiff [-q] file1 file2 or ndiff (-r1 | -r2) < ndiff_output > file1_or_file2 Print a human-friendly file difference report to stdout. Both inter- and intra-line differences are noted. In the second form, recreate file1 (-r1) or file2 (-r2) on stdout, from an ndiff report on stdin. In the first form, if -q ("quiet") is not specified, the first two lines of output are -: file1 +: file2 Each remaining line begins with a two-letter code: "- " line unique to file1 "+ " line unique to file2 " " line common to both files "? " line not present in either input file Lines beginning with "? " attempt to guide the eye to intraline differences, and were not present in either input file. These lines can be confusing if the source files contain tab characters. The first file can be recovered by retaining only lines that begin with " " or "- ", and deleting those 2-character prefixes; use ndiff with -r1. The second file can be recovered similarly, but by retaining only " " and "+ " lines; use ndiff with -r2; or, on Unix, the second file can be recovered by piping the output through sed -n '/^[+ ] /s/^..//p' """ __version__ = 1, 7, 0 import difflib, sys def fail(msg): out = sys.stderr.write out(msg + "\n\n") out(__doc__) return 0 # open a file & return the file object; gripe and return 0 if it # couldn't be opened def fopen(fname): try: return open(fname) except IOError as detail: return fail("couldn't open " + fname + ": " + str(detail)) # open two files & spray the diff to stdout; return false iff a problem def fcompare(f1name, f2name): f1 = fopen(f1name) f2 = fopen(f2name) if not f1 or not f2: return 0 a = f1.readlines(); f1.close() b = f2.readlines(); f2.close() for line in difflib.ndiff(a, b): print(line, end=' ') return 1 # crack args (sys.argv[1:] is normal) & compare; # return false iff a problem def main(args): import getopt try: opts, args = getopt.getopt(args, "qr:") except getopt.error as detail: return fail(str(detail)) noisy = 1 qseen = rseen = 0 for opt, val in opts: if opt == "-q": qseen = 1 noisy = 0 elif opt == "-r": rseen = 1 whichfile = val if qseen and rseen: return fail("can't specify both -q and -r") if rseen: if args: return fail("no args allowed with -r option") if whichfile in ("1", "2"): restore(whichfile) return 1 return fail("-r value must be 1 or 2") if len(args) != 2: return fail("need 2 filename args") f1name, f2name = args if noisy: print('-:', f1name) print('+:', f2name) return fcompare(f1name, f2name) # read ndiff output from stdin, and print file1 (which=='1') or # file2 (which=='2') to stdout def restore(which): restored = difflib.restore(sys.stdin.readlines(), which) sys.stdout.writelines(restored) if __name__ == '__main__': main(sys.argv[1:])