Pythonでテキスト処理:difflibモジュールで差分表示を生成する5つの方法
本記事では、difflib モジュールを用いた差分表示の例を、分かりやすく解説します。
例:2つのテキストファイルの差分比較
以下に、異なる内容を持つ2つのテキストファイル file1.txt
と file2.txt
を用意します。
# file1.txt
This is the first line of file1.txt.
This is the second line of file1.txt.
# file2.txt
This is the first line of file2.txt.
This is the second line of file2.txt.
This is the third line of file2.txt.
これらのファイルの内容を比較し、差分を表示するPythonプログラムは以下の通りです。
from difflib import SequenceMatcher
def compare_files(file1, file2):
"""
2つのファイルを比較し、その差分を表示する関数
Args:
file1: 差分比較対象のファイルパス1
file2: 差分比較対象のファイルパス2
"""
with open(file1, 'r') as f1, open(file2, 'r') as f2:
lines1 = f1.readlines()
lines2 = f2.readlines()
sm = SequenceMatcher(None, lines1, lines2)
for op in sm.get_opcodes():
print(op)
if __name__ == '__main__':
compare_files('file1.txt', 'file2.txt')
このプログラムを実行すると、以下の出力が得られます。
diff_match_line(0, 0, 0)
diff_insert(0, 2, ['This is the third line of file2.txt.\n'])
上記の出力は、file2.txt
のみに存在する3行目が追加されたことを示しています。
difflib モジュールは、SequenceMatcherクラスとDifferクラスという2つの主要なクラスを提供します。
- Differ: SequenceMatcherクラスの出力結果に基づいて、人間が読みやすい差分表示を生成するためのクラスです。
- SequenceMatcher: 2つのシーケンスを比較し、その差異を分析するためのクラスです。
上記の例では、SequenceMatcherクラスを用いて差分を分析し、Differクラスを用いて差分表示を生成しています。
difflib モジュールは、以下のような様々な用途に利用することができます。
- テキストエディタにおける差分マージ
- バージョン管理システムにおける差分表示
- ファイルの差分比較
difflib モジュールは、テキスト処理における強力なツールであり、様々な場面で活用することができます。
上記の説明に加え、difflib モジュールには、以下のような様々な機能が提供されています。
- カスタムな差分表示形式の生成
- HTML形式の差分表示
- ユニファイード差分: コンテキスト差分と類似した形式の差分表示
- コンテキスト差分: 比較対象となる行の前後数行を含めた差分表示
詳細は、difflib モジュールの公式ドキュメントを参照してください。
ファイルの差分比較
from difflib import SequenceMatcher, Differ
def compare_files(file1, file2):
"""
2つのファイルを比較し、その差分を表示する関数
Args:
file1: 差分比較対象のファイルパス1
file2: 差分比較対象のファイルパス2
"""
with open(file1, 'r') as f1, open(file2, 'r') as f2:
lines1 = f1.readlines()
lines2 = f2.readlines()
sm = SequenceMatcher(None, lines1, lines2)
diff = Differ()
print(diff.get_diff(lines1, lines2))
if __name__ == '__main__':
compare_files('file1.txt', 'file2.txt')
このコードでは、Differクラスを用いて、より人間が読みやすい差分表示を生成しています。
コンテキスト差分
以下のコードは、コンテキスト差分を表示する例です。
from difflib import SequenceMatcher, Differ
def compare_files(file1, file2):
"""
2つのファイルを比較し、その差分を表示する関数
Args:
file1: 差分比較対象のファイルパス1
file2: 差分比較対象のファイルパス2
"""
with open(file1, 'r') as f1, open(file2, 'r') as f2:
lines1 = f1.readlines()
lines2 = f2.readlines()
sm = SequenceMatcher(None, lines1, lines2)
diff = Differ(context=3)
print(diff.get_diff(lines1, lines2))
if __name__ == '__main__':
compare_files('file1.txt', 'file2.txt')
このコードでは、Differクラスのcontext
引数に3を指定することで、比較対象となる行の前後3行を含めた差分表示を生成しています。
ユニファイード差分
以下のコードは、ユニファイード差分を表示する例です。
from difflib import SequenceMatcher, Differ
def compare_files(file1, file2):
"""
2つのファイルを比較し、その差分を表示する関数
Args:
file1: 差分比較対象のファイルパス1
file2: 差分比較対象のファイルパス2
"""
with open(file1, 'r') as f1, open(file2, 'r') as f2:
lines1 = f1.readlines()
lines2 = f2.readlines()
sm = SequenceMatcher(None, lines1, lines2)
diff = Differ(unified=True)
print(diff.get_diff(lines1, lines2))
if __name__ == '__main__':
compare_files('file1.txt', 'file2.txt')
このコードでは、Differクラスのunified
引数をTrueに設定することで、ユニファイード差分表示を生成しています。
以下のコードは、HTML形式の差分表示を生成する例です。
from difflib import SequenceMatcher, HtmlDiff
def compare_files(file1, file2):
"""
2つのファイルを比較し、その差分をHTML形式で表示する関数
Args:
file1: 差分比較対象のファイルパス1
file2: 差分比較対象のファイルパス2
"""
with open(file1, 'r') as f1, open(file2, 'r') as f2:
lines1 = f1.readlines()
lines2 = f2.readlines()
sm = SequenceMatcher(None, lines1, lines2)
diff = HtmlDiff()
print(diff.make_file(lines1, lines2))
if __name__ == '__main__':
compare_files('file1.txt', 'file2.txt')
このコードでは、HtmlDiffクラスを用いて、HTML形式の差分表示を生成しています。
モジュール
unidecode モジュールは、Unicode文字列をASCII文字列に変換するためのライブラリです。difflib モジュールと異なり、言語間で異なる文字エンコーディングを使用するテキストを比較する場合に役立ちます。
import unidecode
from difflib import SequenceMatcher
def compare_texts(text1, text2):
"""
2つのテキストを比較し、その差分を表示する関数
Args:
text1: 比較対象のテキスト1
text2: 比較対象のテキスト2
"""
normalized_text1 = unidecode.unidecode(text1)
normalized_text2 = unidecode.unidecode(text2)
lines1 = normalized_text1.splitlines()
lines2 = normalized_text2.splitlines()
sm = SequenceMatcher(None, lines1, lines2)
for op in sm.get_opcodes():
print(op)
if __name__ == '__main__':
text1 = "This is a sample text with accented characters. (éèê)"
text2 = "This is another sample text without accented characters."
compare_texts(text1, text2)
myersdiff モジュールは、2つのシーケンス(文字列、リストなど)の最短編集距離を計算するためのライブラリです。difflib モジュールよりも高速に動作する場合があり、特に長いテキストを比較する場合に役立ちます。
import myersdiff
def compare_texts(text1, text2):
"""
2つのテキストを比較し、その差分を表示する関数
Args:
text1: 比較対象のテキスト1
text2: 比較対象のテキスト2
"""
lines1 = text1.splitlines()
lines2 = text2.splitlines()
diff = myersdiff.diff(lines1, lines2)
for op, data in diff:
print(op, data)
if __name__ == '__main__':
text1 = "This is a sample text with some differences."
text2 = "This is another sample text with some different words."
compare_texts(text1, text2)
モジュール
fuzzywuzzy モジュールは、2つの文字列の類似度を計算するためのライブラリです。difflib モジュールとは異なり、完全一致ではなく、部分一致に基づいた比較を行うことができます。
import fuzzywuzzy
from difflib import SequenceMatcher
def compare_texts(text1, text2):
"""
2つのテキストを比較し、その類似度と差分を表示する関数
Args:
text1: 比較対象のテキスト1
text2: 比較対象のテキスト2
"""
ratio = fuzzywuzzy.ratio(text1, text2)
print(f"類似度: {ratio}%")
lines1 = text1.splitlines()
lines2 = text2.splitlines()
sm = SequenceMatcher(None, lines1, lines2)
for op in sm.get_opcodes():
print(op)
if __name__ == '__main__':
text1 = "This is a sample text with some differences."
text2 = "This is another sample text with some different words."
compare_texts(text1, text2)
カスタムロジック
上記で紹介したライブラリ以外にも、状況に応じてカスタムロジックを開発することも可能です。例えば、特定のパターンに基づいた比較や、複雑な編集距離の計算が必要な場合などに有効です。