difflib --- Người trợ giúp tính toán delta

Source code: Lib/difflib.py


Mô-đun này cung cấp các lớp và chức năng để so sánh trình tự. Ví dụ: nó có thể được sử dụng để so sánh các tệp và có thể tạo ra thông tin về sự khác biệt của tệp ở các định dạng khác nhau, bao gồm HTML, ngữ cảnh và các khác biệt thống nhất. Để so sánh các thư mục và tập tin, hãy xem thêm mô-đun filecmp.

class difflib.SequenceMatcher

Đây là một lớp linh hoạt để so sánh các cặp chuỗi thuộc bất kỳ loại nào, miễn là các phần tử của chuỗi là hashable. Thuật toán cơ bản có trước và lạ hơn một chút so với thuật toán được Ratcliff và Obershelp xuất bản vào cuối những năm 1980 dưới tên hyperbol là "khớp mẫu hình cử chỉ". Ý tưởng là tìm chuỗi con liền kề dài nhất không chứa phần tử "rác"; những phần tử "rác" này là những phần tử không thú vị theo một nghĩa nào đó, chẳng hạn như các dòng trống hoặc khoảng trắng. (Xử lý rác là một phần mở rộng của thuật toán Ratcliff và Obershelp.) Ý tưởng tương tự sau đó được áp dụng đệ quy cho các phần của chuỗi ở bên trái và bên phải của chuỗi con phù hợp. Điều này không mang lại trình tự chỉnh sửa tối thiểu nhưng có xu hướng mang lại kết quả phù hợp với mọi người.

Timing: Thuật toán Ratcliff-Obershelp cơ bản là thời gian bậc ba trong trường hợp xấu nhất và thời gian bậc hai trong trường hợp dự kiến. SequenceMatcher là thời gian bậc hai cho trường hợp xấu nhất và có hành vi trong trường hợp dự kiến ​​phụ thuộc một cách phức tạp vào số phần tử chung của các chuỗi; trường hợp tốt nhất thời gian là tuyến tính.

Automatic junk heuristic: SequenceMatcher hỗ trợ phương pháp phỏng đoán tự động coi các mục trong chuỗi nhất định là rác. Heuristic đếm số lần mỗi mục riêng lẻ xuất hiện trong chuỗi. Nếu các bản sao của một mục (sau mục đầu tiên) chiếm hơn 1% trình tự và trình tự dài ít nhất 200 mục thì mục này được đánh dấu là "phổ biến" và được coi là rác vì mục đích khớp trình tự. Bạn có thể tắt phương pháp phỏng đoán này bằng cách đặt đối số autojunk thành False khi tạo SequenceMatcher.

Thay đổi trong phiên bản 3.2: Đã thêm tham số autojunk.

class difflib.Differ

Đây là lớp để so sánh trình tự các dòng văn bản và tạo ra những điểm khác biệt hoặc vùng đồng bằng mà con người có thể đọc được. Differ sử dụng SequenceMatcher để so sánh trình tự các dòng và so sánh trình tự các ký tự trong các dòng tương tự (gần khớp).

Mỗi dòng của delta Differ bắt đầu bằng mã gồm hai chữ cái:

Ý nghĩa

'- '

dòng duy nhất cho chuỗi 1

'+ '

dòng duy nhất cho chuỗi 2

'  '

dòng chung cho cả hai chuỗi

'? '

dòng không có trong cả hai chuỗi đầu vào

Các dòng bắt đầu bằng '?' cố gắng hướng mắt đến những khác biệt trong dòng và không xuất hiện trong cả hai chuỗi đầu vào. Những dòng này có thể gây nhầm lẫn nếu chuỗi chứa các ký tự khoảng trắng, chẳng hạn như dấu cách, tab hoặc dấu ngắt dòng.

class difflib.HtmlDiff

Lớp này có thể được sử dụng để tạo một bảng HTML (hoặc một tệp HTML hoàn chỉnh có chứa bảng) hiển thị so sánh văn bản cạnh nhau, từng dòng với các điểm nổi bật thay đổi giữa dòng và nội dòng. Bảng có thể được tạo ở chế độ khác biệt đầy đủ hoặc theo ngữ cảnh.

Hàm tạo cho lớp này là:

__init__(tabsize=8, wrapcolumn=None, linejunk=None, charjunk=IS_CHARACTER_JUNK)

Khởi tạo phiên bản của HtmlDiff.

tabsize là một đối số từ khóa tùy chọn để chỉ định khoảng cách giữa các điểm dừng tab và mặc định là 8.

wrapcolumn là từ khóa tùy chọn để chỉ định số cột trong đó các dòng bị ngắt và được ngắt dòng, mặc định là None khi các dòng không được ngắt dòng.

linejunkcharjunk là các đối số từ khóa tùy chọn được chuyển vào ndiff() (được HtmlDiff sử dụng để tạo ra sự khác biệt giữa HTML). Xem tài liệu ndiff() để biết các mô tả và giá trị mặc định của đối số.

Các phương pháp sau đây là công khai:

make_file(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5, *, charset='utf-8')

So sánh fromlinestolines (danh sách các chuỗi) và trả về một chuỗi là tệp HTML hoàn chỉnh chứa một bảng hiển thị sự khác biệt theo từng dòng với các thay đổi giữa các dòng và nội dòng được tô sáng.

fromdesctodesc là các đối số từ khóa tùy chọn để chỉ định các chuỗi tiêu đề cột từ/đến tệp (cả hai đều mặc định là một chuỗi trống).

contextnumlines đều là đối số từ khóa tùy chọn. Đặt context thành True khi hiển thị sự khác biệt về ngữ cảnh, nếu không thì mặc định là False để hiển thị toàn bộ tệp. numlines mặc định là 5. Khi contextTrue numlines kiểm soát số lượng dòng ngữ cảnh bao quanh các điểm nổi bật khác nhau. Khi contextFalse numlines kiểm soát số dòng được hiển thị trước điểm đánh dấu sự khác biệt khi sử dụng siêu liên kết "tiếp theo" (việc đặt thành 0 sẽ khiến các siêu liên kết "tiếp theo" đặt điểm nổi bật khác biệt tiếp theo ở đầu trình duyệt mà không có bất kỳ bối cảnh dẫn đầu nào).

Ghi chú

fromdesctodesc được hiểu là HTML không thoát và phải được thoát đúng cách trong khi nhận đầu vào từ các nguồn không đáng tin cậy.

Thay đổi trong phiên bản 3.5: đối số chỉ từ khóa charset đã được thêm vào. Bộ ký tự mặc định của tài liệu HTML đã thay đổi từ 'ISO-8859-1' thành 'utf-8'.

make_table(fromlines, tolines, fromdesc='', todesc='', context=False, numlines=5)

So sánh fromlinestolines (danh sách các chuỗi) và trả về một chuỗi là một bảng HTML hoàn chỉnh hiển thị sự khác biệt theo từng dòng với các thay đổi giữa các dòng và nội dòng được tô sáng.

Các đối số cho phương thức này giống với các đối số cho phương thức make_file().

difflib.context_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')

So sánh ab (danh sách các chuỗi); trả về một delta (generator tạo các dòng delta) ở định dạng khác biệt ngữ cảnh.

Khác biệt ngữ cảnh là một cách nhỏ gọn để chỉ hiển thị những dòng đã thay đổi cộng với một vài dòng ngữ cảnh. Những thay đổi được hiển thị theo kiểu trước/sau. Số lượng dòng ngữ cảnh được đặt bởi n, mặc định là ba.

Theo mặc định, các dòng điều khiển khác biệt (những dòng có *** hoặc ---) được tạo bằng một dòng mới ở cuối. Điều này hữu ích để các đầu vào được tạo từ io.IOBase.readlines() tạo ra các khác biệt phù hợp để sử dụng với io.IOBase.writelines() vì cả đầu vào và đầu ra đều có dòng mới ở cuối.

Đối với các đầu vào không có dòng mới ở cuối, hãy đặt đối số lineterm thành "" để đầu ra sẽ không có dòng mới một cách thống nhất.

Định dạng khác biệt ngữ cảnh thường có tiêu đề cho tên tệp và thời gian sửa đổi. Bất kỳ hoặc tất cả những điều này có thể được chỉ định bằng cách sử dụng chuỗi cho fromfile, tofile, fromfiledatetofiledate. Thời gian sửa đổi thường được thể hiện ở định dạng ISO 8601. Nếu không được chỉ định, các chuỗi sẽ mặc định là khoảng trống.

>>> 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

Xem Giao diện dòng lệnh cho difflib để biết ví dụ chi tiết hơn.

difflib.get_close_matches(word, possibilities, n=3, cutoff=0.6)

Trả về danh sách các kết quả phù hợp "đủ tốt" tốt nhất. word là một chuỗi cần có các kết quả khớp gần nhau (thường là một chuỗi) và possibilities là danh sách các chuỗi để khớp với word (thường là danh sách các chuỗi).

Đối số tùy chọn n (3 mặc định) là số lượng kết quả khớp gần tối đa cần trả về; n phải lớn hơn 0.

Đối số tùy chọn cutoff (0.6 mặc định) là một số float trong phạm vi [0, 1]. Những khả năng không đạt điểm tối thiểu tương tự như word sẽ bị bỏ qua.

Các kết quả phù hợp nhất (không quá n) trong số các khả năng sẽ được trả về trong một danh sách, được sắp xếp theo điểm tương đồng, giống nhất trước tiê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']
difflib.ndiff(a, b, linejunk=None, charjunk=IS_CHARACTER_JUNK)

So sánh ab (danh sách các chuỗi); trả về một delta kiểu Differ- (một generator tạo ra các đường delta).

Các tham số từ khóa tùy chọn linejunkcharjunk là các hàm lọc (hoặc None):

linejunk: Hàm chấp nhận một đối số chuỗi đơn và trả về true nếu chuỗi đó là rác hoặc trả về false nếu không. Mặc định là None. Ngoài ra còn có một hàm cấp mô-đun IS_LINE_JUNK(), giúp lọc ra các dòng không có ký tự hiển thị, ngoại trừ nhiều nhất một ký tự băm ('#') -- tuy nhiên, lớp SequenceMatcher cơ bản thực hiện phân tích động về những dòng nào xuất hiện thường xuyên đến mức tạo thành nhiễu và điều này thường hoạt động tốt hơn so với việc sử dụng chức năng này.

charjunk: Hàm chấp nhận một ký tự (chuỗi có độ dài 1) và trả về nếu ký tự đó là rác hoặc trả về sai nếu không. Mặc định là hàm cấp mô-đun IS_CHARACTER_JUNK(), chức năng này lọc các ký tự khoảng trắng (trống hoặc tab; việc thêm dòng mới vào đây là một ý tưởng tồi!).

>>> 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
difflib.restore(sequence, which)

Trả về một trong hai chuỗi đã tạo ra delta.

Cho một sequence được tạo bởi Differ.compare() hoặc ndiff(), trích xuất các dòng có nguồn gốc từ tệp 1 hoặc 2 (tham số which), loại bỏ các tiền tố dòng.

Ví dụ:

>>> 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
difflib.unified_diff(a, b, fromfile='', tofile='', fromfiledate='', tofiledate='', n=3, lineterm='\n')

So sánh ab (danh sách các chuỗi); trả về một delta (generator tạo các dòng delta) ở định dạng khác biệt thống nhất.

Khác biệt thống nhất là một cách nhỏ gọn để chỉ hiển thị các dòng đã thay đổi cộng với một vài dòng ngữ cảnh. Các thay đổi được hiển thị theo kiểu nội tuyến (thay vì các khối trước/sau riêng biệt). Số lượng dòng ngữ cảnh được đặt bởi n, mặc định là ba.

Theo mặc định, các dòng điều khiển khác biệt (những dòng có ---, +++ hoặc @@) được tạo bằng một dòng mới ở cuối. Điều này hữu ích để các đầu vào được tạo từ io.IOBase.readlines() tạo ra các khác biệt phù hợp để sử dụng với io.IOBase.writelines() vì cả đầu vào và đầu ra đều có dòng mới ở cuối.

Đối với các đầu vào không có dòng mới ở cuối, hãy đặt đối số lineterm thành "" để đầu ra sẽ không có dòng mới một cách thống nhất.

Định dạng khác biệt thống nhất thường có tiêu đề cho tên tệp và thời gian sửa đổi. Bất kỳ hoặc tất cả những điều này có thể được chỉ định bằng cách sử dụng chuỗi cho fromfile, tofile, fromfiledatetofiledate. Thời gian sửa đổi thường được thể hiện ở định dạng ISO 8601. Nếu không được chỉ định, các chuỗi sẽ mặc định là khoảng trống.

>>> 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

Xem Giao diện dòng lệnh cho difflib để biết ví dụ chi tiết hơn.

difflib.diff_bytes(dfunc, a, b, fromfile=b'', tofile=b'', fromfiledate=b'', tofiledate=b'', n=3, lineterm=b'\n')

So sánh ab (danh sách các đối tượng byte) bằng dfunc; mang lại một chuỗi các dòng delta (cũng là byte) ở định dạng được trả về bởi dfunc. dfunc phải có thể gọi được, thường là unified_diff() hoặc context_diff().

Cho phép bạn so sánh dữ liệu với mã hóa không xác định hoặc không nhất quán. Tất cả đầu vào ngoại trừ n phải là đối tượng byte, không phải str. Hoạt động bằng cách chuyển đổi dễ dàng tất cả đầu vào (ngoại trừ n) thành str và gọi dfunc(a, b, fromfile, tofile, fromfiledate, tofiledate, n, lineterm). Sau đó, đầu ra của dfunc được chuyển đổi trở lại thành byte, do đó, các dòng delta mà bạn nhận được có cùng mã hóa không xác định/không nhất quán như ab.

Added in version 3.5.

difflib.IS_LINE_JUNK(line)

Trả về True cho các dòng có thể bỏ qua. Dòng line không thể bỏ qua nếu line trống hoặc chỉ chứa một '#', nếu không thì không thể bỏ qua. Được sử dụng làm mặc định cho tham số linejunk trong ndiff() ở các phiên bản cũ hơn.

difflib.IS_CHARACTER_JUNK(ch)

Trả về True cho các ký tự không thể bỏ qua. Ký tự ch không thể bỏ qua nếu ch là khoảng trắng hoặc tab, nếu không thì không thể bỏ qua. Được sử dụng làm mặc định cho tham số charjunk trong ndiff().

Xem thêm

Pattern Matching: The Gestalt Approach

Thảo luận về một thuật toán tương tự của John W. Ratcliff và D. E. Metzener. Điều này đã được công bố trên Tạp chí của Tiến sĩ Dobb vào tháng 7 năm 1988.

Đối tượng SequenceMatcher

Lớp SequenceMatcher có hàm tạo này:

class difflib.SequenceMatcher(isjunk=None, a='', b='', autojunk=True)

Đối số tùy chọn isjunk phải là None (mặc định) hoặc hàm một đối số nhận một phần tử chuỗi và trả về true khi và chỉ khi phần tử đó là "rác" và nên được bỏ qua. Việc chuyển None cho isjunk tương đương với việc chuyển lambda x: False; nói cách khác, không có phần tử nào bị bỏ qua. Ví dụ: vượt qua:

lambda x: x trong " \t"

nếu bạn đang so sánh các dòng dưới dạng chuỗi ký tự và không muốn đồng bộ hóa trên các khoảng trống hoặc tab cứng.

Các đối số tùy chọn ab là các chuỗi được so sánh; cả hai đều mặc định là chuỗi trống. Các phần tử của cả hai chuỗi phải là hashable.

Đối số tùy chọn autojunk có thể được sử dụng để vô hiệu hóa tính năng phỏng đoán rác tự động.

Thay đổi trong phiên bản 3.2: Đã thêm tham số autojunk.

Các đối tượng SequenceMatcher nhận ba thuộc tính dữ liệu: bjunk là tập hợp các phần tử của bisjunkTrue; bpopular là tập hợp các phần tử không phải rác được heuristic coi là phổ biến (nếu nó không bị vô hiệu hóa); b2j là một lệnh ánh xạ các phần tử còn lại của b vào danh sách các vị trí nơi chúng xuất hiện. Cả ba đều được đặt lại bất cứ khi nào b được đặt lại bằng set_seqs() hoặc set_seq2().

Added in version 3.2: Thuộc tính bjunkbpopular.

Các đối tượng SequenceMatcher có các phương thức sau:

set_seqs(a, b)

Đặt hai trình tự để so sánh.

SequenceMatcher tính toán và lưu trữ thông tin chi tiết về chuỗi thứ hai, vì vậy nếu bạn muốn so sánh một chuỗi với nhiều chuỗi, hãy sử dụng set_seq2() để đặt chuỗi thường được sử dụng một lần và gọi set_seq1() liên tục, một lần cho mỗi chuỗi khác.

set_seq1(a)

Đặt trình tự đầu tiên để so sánh. Trình tự thứ hai được so sánh không thay đổi.

set_seq2(b)

Đặt trình tự thứ hai để so sánh. Trình tự đầu tiên được so sánh không thay đổi.

find_longest_match(alo=0, ahi=None, blo=0, bhi=None)

Tìm khối khớp dài nhất trong a[alo:ahi]b[blo:bhi].

Nếu isjunk bị bỏ qua hoặc None, find_longest_match() trả về (i, j, k) sao cho a[i:i+k] bằng b[j:j+k], trong đó alo <= i <= i+k <= ahiblo <= j <= j+k <= bhi. Đối với tất cả (i', j', k') đáp ứng các điều kiện đó, các điều kiện bổ sung k >= k', i <= i' và nếu i == i', j <= j' cũng được đáp ứng. Nói cách khác, trong tất cả các khối khớp tối đa, hãy trả về khối bắt đầu sớm nhất trong a và trong tất cả các khối khớp tối đa bắt đầu sớm nhất trong a, hãy trả về khối bắt đầu sớm nhất trong b.

>>> s = SequenceMatcher(None, " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
Match(a=0, b=4, size=5)

Nếu isjunk được cung cấp, đầu tiên khối phù hợp dài nhất được xác định như trên, nhưng với hạn chế bổ sung là không có phần tử rác nào xuất hiện trong khối. Sau đó, khối đó được mở rộng hết mức có thể bằng cách kết hợp (chỉ) các phần tử rác ở cả hai bên. Vì vậy, khối kết quả không bao giờ khớp với rác ngoại trừ khi rác giống hệt nhau nằm liền kề với một kết quả thú vị.

Đây là ví dụ tương tự như trước, nhưng coi khoảng trống là rác. Điều đó ngăn ' abcd' khớp trực tiếp với ' abcd' ở phần cuối của chuỗi thứ hai. Thay vào đó, chỉ 'abcd' mới có thể khớp và khớp với 'abcd' ngoài cùng bên trái trong chuỗi thứ hai:

>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")
>>> s.find_longest_match(0, 5, 0, 9)
Match(a=1, b=0, size=4)

Nếu không có khối nào khớp, kết quả này sẽ trả về (alo, blo, 0).

Phương thức này trả về named tuple Match(a, b, size).

Thay đổi trong phiên bản 3.9: Đã thêm đối số mặc định.

get_matching_blocks()

Trả về danh sách các bộ ba mô tả các chuỗi con khớp không chồng chéo. Mỗi bộ ba có dạng (i, j, n) và có nghĩa là a[i:i+n] == b[j:j+n]. Bộ ba đang tăng đơn điệu trong ij.

Bộ ba cuối cùng là giả và có giá trị (len(a), len(b), 0). Đây là bộ ba duy nhất có n == 0. Nếu (i, j, n)(i', j', n') là bộ ba liền kề trong danh sách và bộ thứ hai không phải là bộ ba cuối cùng trong danh sách, thì i+n < i' hoặc j+n < j'; nói cách khác, các bộ ba liền kề luôn mô tả các khối bằng nhau không liền kề.

>>> s = SequenceMatcher(Không , "abxcd", "abcd")
>>> s.get_matching_blocks()
[Trận đấu(a=0, b=0, size=2), Trận đấu(a=3, b=2, size=2), Trận đấu(a=5, b=4, size=0)]
get_opcodes()

Trả về danh sách 5 bộ dữ liệu mô tả cách biến a thành b. Mỗi bộ có dạng (tag, i1, i2, j1, j2). Bộ đầu tiên có i1 == j1 == 0, và các bộ còn lại có i1 bằng i2 từ bộ trước đó, và tương tự, j1 bằng j2 trước đó.

Các giá trị tag là các chuỗi, có ý nghĩa sau:

Giá trị

Ý nghĩa

'replace'

a[i1:i2] nên được thay thế bằng b[j1:j2].

'delete'

a[i1:i2] nên xóa đi. Lưu ý rằng j1 == j2 trong trường hợp này.

'insert'

b[j1:j2] nên được chèn vào a[i1:i1]. Lưu ý rằng i1 == i2 trong trường hợp này.

'equal'

a[i1:i2] == b[j1:j2] (các chuỗi con bằng nhau).

Ví dụ:

>>> a = "qabxcd"
>>> b = "abycdf"
>>> s = SequenceMatcher(Không, a, b)
>>> cho tag, i1, i2, j1, j2 trong s.get_opcodes():
... print('{:7} a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format(
... thẻ, i1, i2, j1, j2, a[i1:i2], b[j1:j2]))
xóa a[0:1] --> b[0:0] 'q' --> ''
bằng a[1:3] --> b[0:2] 'ab' --> 'ab'
thay thế a[3:4] --> b[2:3] 'x' --> 'y'
bằng a[4:6] --> b[3:5] 'cd' --> 'cd'
chèn a[6:6] --> b[5:6] '' --> 'f'
get_grouped_opcodes(n=3)

Trả về một nhóm generator có dòng ngữ cảnh lên tới n.

Bắt đầu với các nhóm được get_opcodes() trả về, phương pháp này tách ra các cụm thay đổi nhỏ hơn và loại bỏ các phạm vi can thiệp không có thay đổi.

Các nhóm được trả về có cùng định dạng với get_opcodes().

ratio()

Trả về thước đo độ tương tự của chuỗi dưới dạng số float trong phạm vi [0, 1].

Trong đó T là tổng số phần tử trong cả hai chuỗi và M là số lượng trùng khớp, giá trị này là 2,0*M / T. Lưu ý rằng đây là 1.0 nếu các chuỗi giống hệt nhau và 0.0 nếu chúng không có điểm chung.

Việc tính toán này rất tốn kém nếu get_matching_blocks() hoặc get_opcodes() chưa được gọi, trong trường hợp đó bạn có thể muốn thử quick_ratio() hoặc real_quick_ratio() trước để có giới hạn trên.

Ghi chú

Thận trọng: Kết quả của lệnh gọi ratio() có thể phụ thuộc vào thứ tự của các đối số. Ví dụ:

>>> SequenceMatcher(Không , 'thủy triều', 'ăn kiêng').ratio()
0,25
>>> SequenceMatcher(Không , 'ăn kiêng', 'thủy triều').ratio()
0,5
quick_ratio()

Trả lại giới hạn trên của ratio() tương đối nhanh chóng.

real_quick_ratio()

Trả lại giới hạn trên của ratio() rất nhanh.

Ba phương thức trả về tỷ lệ khớp với tổng số ký tự có thể cho kết quả khác nhau do mức độ gần đúng khác nhau, mặc dù quick_ratio()real_quick_ratio() luôn lớn ít nhất bằng ratio():

>>> s = SequenceMatcher(None, "abcd", "bcde")
>>> s.ratio()
0.75
>>> s.quick_ratio()
0.75
>>> s.real_quick_ratio()
1.0

Ví dụ về SequenceMatcher

Ví dụ này so sánh hai chuỗi, coi các khoảng trống là "rác":

>>> s = SequenceMatcher(lambda x: x == " ",
...                     "private Thread currentThread;",
...                     "private volatile Thread currentThread;")

ratio() trả về một số float trong [0, 1], đo lường mức độ giống nhau của các chuỗi. Theo nguyên tắc chung, giá trị ratio() trên 0,6 có nghĩa là các chuỗi gần giống nhau:

>>> print(round(s.ratio(), 3))
0.866

Nếu bạn chỉ quan tâm đến vị trí của các chuỗi khớp nhau, get_matching_blocks() sẽ rất hữu ích:

>>> 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

Lưu ý rằng bộ dữ liệu cuối cùng được get_matching_blocks() trả về luôn là một giá trị giả, (len(a), len(b), 0) và đây là trường hợp duy nhất trong đó phần tử bộ dữ liệu cuối cùng (số phần tử khớp) là 0.

Nếu bạn muốn biết cách thay đổi chuỗi đầu tiên thành chuỗi thứ hai, hãy sử dụng 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]

Xem thêm

Đối tượng khác nhau

Lưu ý rằng các delta được tạo ra bởi Differ- không khẳng định đó là khác biệt của minimal. Ngược lại, những khác biệt tối thiểu thường phản trực giác, bởi vì chúng đồng bộ hóa ở mọi nơi có thể, đôi khi vô tình trùng khớp cách nhau 100 trang. Việc hạn chế các điểm đồng bộ đối với các kết quả khớp liền kề sẽ bảo tồn một số khái niệm về địa phương, với chi phí không thường xuyên là tạo ra sự khác biệt dài hơn.

Lớp Differ có hàm tạo này:

class difflib.Differ(linejunk=None, charjunk=None)

Các tham số từ khóa tùy chọn linejunkcharjunk dành cho các chức năng lọc (hoặc None):

linejunk: Hàm chấp nhận một đối số chuỗi đơn và trả về true nếu chuỗi đó là chuỗi rác. Mặc định là None, nghĩa là không có dòng nào bị coi là rác.

charjunk: Hàm chấp nhận một đối số ký tự đơn (chuỗi có độ dài 1) và trả về true nếu ký tự đó là ký tự rác. Mặc định là None, nghĩa là không có ký tự nào bị coi là rác.

Các chức năng lọc rác này tăng tốc độ khớp để tìm ra sự khác biệt và không làm bỏ qua bất kỳ dòng hoặc ký tự khác nhau nào. Đọc mô tả về tham số isjunk của phương thức find_longest_match() để được giải thích.

Các đối tượng Differ được sử dụng (được tạo delta) thông qua một phương thức duy nhất:

compare(a, b)

So sánh hai chuỗi dòng và tạo delta (một chuỗi các dòng).

Mỗi chuỗi phải chứa các chuỗi một dòng riêng lẻ kết thúc bằng dòng mới. Các chuỗi như vậy có thể thu được từ phương pháp readlines() của các đối tượng giống như tệp. Delta được tạo cũng bao gồm các chuỗi kết thúc bằng dòng mới, sẵn sàng để được in nguyên trạng thông qua phương thức writelines() của một đối tượng giống như tệp.

Ví dụ khác biệt

Ví dụ này so sánh hai văn bản. Trước tiên, chúng tôi thiết lập các văn bản, chuỗi các chuỗi một dòng riêng lẻ kết thúc bằng dòng mới (các chuỗi như vậy cũng có thể được lấy từ phương thức readlines() của các đối tượng giống như tệp):

>>> 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)

Tiếp theo chúng ta khởi tạo một đối tượng Differ:

>>> d = Differ()

Lưu ý rằng khi khởi tạo một đối tượng Differ, chúng ta có thể chuyển các hàm để lọc ra dòng và ký tự "rác". Xem hàm tạo Differ() để biết chi tiết.

Cuối cùng, chúng tôi so sánh hai:

>>> result = list(d.compare(text1, text2))

result là một danh sách các chuỗi, vì vậy hãy in nó ra:

>>> 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']

Là một chuỗi nhiều dòng, nó trông như thế này:

>>> 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.

Giao diện dòng lệnh cho difflib

Ví dụ này cho thấy cách sử dụng difflib để tạo tiện ích giống diff.

""" Giao diện dòng lệnh cho difflib.py cung cấp các khác biệt ở bốn định dạng:

* ndiff: liệt kê mọi dòng và đánh dấu những thay đổi giữa các dòng.
* bối cảnh: làm nổi bật các cụm thay đổi ở định dạng trước/sau.
* thống nhất: làm nổi bật các cụm thay đổi ở định dạng nội tuyến.
* html: tạo ra sự so sánh song song với những điểm nổi bật của thay đổi.

"""

nhập sys, os, difflib, argparse
nhập ngày giờ dưới dạng dt

def file_mtime(đường dẫn):
    t = dt.datetime.fromtimestamp(os.stat(path).st_mtime,
                                  dt.timezone.utc)
    trả về t.astimezone().isoformat()

chắc chắn chính():

    trình phân tích  pháp = argparse.ArgumentParser()
    parser.add_argument('-c', action='store_true', default=False,
                        help='Tạo khác biệt về định dạng ngữ cảnh (mặc định)')
    parser.add_argument('-u', action='store_true', default=False,
                        help='Tạo ra sự khác biệt về định dạng thống nhất')
    parser.add_argument('-m', action='store_true', default=False,
                        help='Tạo ra HTML khác biệt cạnh nhau '
                             '(có thể sử dụng kết hợp -c và -l)')
    parser.add_argument('-n', action='store_true', default=False,
                        help='Tạo ra sự khác biệt về định dạng ndiff')
    Parser.add_argument('-l', '--lines', type=int, default=3,
                        help='Đặt số dòng ngữ cảnh (mặc định 3)')
    parser.add_argument('fromfile')
    trình phân tích  pháp.add_argument('tofile')
    tùy chọn = trình phân tích  pháp.parse_args()

    n = tùy chọn.lines
    fromfile = tùy chọn.fromfile
    tofile = tùy chọn.tofile

    fromdate = file_mtime(fromfile)
    hôm nay = file_mtime(tofile)
    với open(fromfile)  ff:
        fromlines = ff.readlines()
    với open(tofile)  tf:
        tolines = tf.readlines()

    nếu tùy chọn.u:
        diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)
    tùy chọn Elif.n:
        diff = difflib.ndiff(fromlines, tolines)
    tùy chọn Elif.m:
        diff = difflib.HtmlDiff().make_file(fromlines,tolines,fromfile,tofile,context=options.c,numlines=n)
    khác:
        diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)

    sys.stdout.writelines(diff)

nếu __name__ == '__main__':
    chính()

ví dụ khác

Ví dụ này cho thấy cách sử dụng difflib.ndiff().

"""ndiff [-q] file1 file2
    hoặc
ndiff (-r1 | -r2) < ndiff_output > file1_or_file2

In báo cáo khác biệt về tệp thân thiện với con người tới thiết bị xuất chuẩn.  Cả hai liên
và sự khác biệt trong dòng được ghi nhận.  Ở dạng thứ hai, tạo lại file1
(-r1) hoặc file2 (-r2) trên thiết bị xuất chuẩn, từ báo cáo ndiff trên stdin.

Ở dạng đầu tiên, nếu -q ("im lặng") không được chỉ định thì hai dòng đầu tiên
của đầu ra là

-: tập tin1
+: tập tin2

Mỗi dòng còn lại bắt đầu bằng mã gồm hai chữ cái:

    "- " dòng duy nhất cho file1
    dòng "+" duy nhất cho file2
    " " dòng chung cho cả hai tập tin
    "? " dòng không có trong cả hai tập tin đầu vào

Các dòng bắt đầu bằng "? " cố gắng hướng mắt vào nội tuyến
sự khác biệt và không có trong cả hai tệp đầu vào.  Những dòng này có thể
khó hiểu nếu tệp nguồn chứa ký tự tab.

Tệp đầu tiên có thể được phục hồi bằng cách chỉ giữ lại các dòng bắt đầu bằng
" " hoặc "- " và xóa các tiền tố 2 ký tự đó; sử dụng ndiff với -r1.

Tệp thứ hai có thể được phục hồi tương tự nhưng bằng cách chỉ giữ lại " " và
dòng "+"; sử dụng ndiff với -r2; hoặc, trên Unix, tệp thứ hai có thể là
được phục hồi bằng cách dẫn đầu ra qua

    sed -n '/^[+ ] /s/^..//p'
"""

__phiên bản__ = 1, 7, 0

nhập difflib, sys

def thất bại (tin nhắn):
    out = sys.stderr.write
    out(tin nhắn + "\n\n")
    ra(__doc__)
    trở về 0

# open một tệp và trả về đối tượng tệp; nắm và trả về 0 nếu nó
# couldn không mở được
def fopen(tên):
    thử:
        quay lại mở(fname)
    ngoại trừ IOError như chi tiết:
        return failed("không thể mở được " + fname + ": " + str(detail))

# open hai tệp & phun phần khác biệt vào thiết bị xuất chuẩn; trả về sai nếu có vấn đề
def fcompare(f1name, f2name):
    f1 = fopen(f1name)
    f2 = fopen(f2name)
    nếu không phải f1 hay không f2:
        trở về 0

    a = f1.readlines(); f1.close()
    b = f2.readlines(); f2.close()
    cho dòng trong Difflib.ndiff(a, b):
        in(dòng, kết thúc=' ')

    trở lại 1

# crack args (sys.argv[1:] là bình thường) & so sánh;
# return sai nếu có vấn đề

def chính (args):
    nhập khẩu
    thử:
        opts, args = getopt.getopt(args, "qr:")
    ngoại trừ getopt.error như chi tiết:
        trả về thất bại(str(chi tiết))
    ồn ào = 1
    qseen = rseen = 0
    cho opt, val trong opt:
        nếu chọn == "-q":
            qseen = 1
            ồn ào = 0
        lựa chọn Elif == "-r":
            rseen = 1
            tập tin nào = val
    nếu qseen  rseen:
        return failed("không thể chỉ định cả -q và -r")
    nếu nhìn thấy:
        nếu lập luận:
            return failed("không cho phép đối số với tùy chọn -r")
        nếu tệp nào trong ("1", "2"):
            khôi phục (tệp nào)
            trở lại 1
        return failed("-r value phải là 1 hoặc 2")
    nếu len(args) != 2:
        return failed("cần 2 tên file args")
    f1name, f2name = đối số
    nếu ồn ào:
        in('-:', f1name)
        in('+:', f2name)
    trả về fcompare(f1name, f2name)

# read ndiff xuất ra từ stdin và in file1 (which=='1') hoặc
# file2 (which=='2') thành thiết bị xuất chuẩn

khôi phục chắc chắn (trong đó):
    được khôi phục = difflib.restore(sys.stdin.readlines(), which)
    sys.stdout.writelines (đã khôi phục)

nếu __name__ == '__main__':
    chính(sys.argv[1:])