Pythonのdifflibモジュールを用いて複数行テキストどうしの差分を取得

Last modified: 2021-10-13

Pythonにはdifflibというモジュールがあり、これを用いると複数行のテキストデータどうしをdiffコマンドのように比較し、同コマンドで得られるような形式で結果を得ることができる。

簡単な例として、ここではunified形式の差分を取ってみることにする。

テキストの差分の取り方

比較対象の2つのテキストデータはそれぞれ文字列型のみを含むリストやタプルで、1つの行が1つの要素という形である必要がある。

["1行目", "2行目", "3行目"]

改行を含む複数行の文字列がある場合、そのメンバ関数splitlines()で分割したものが使える。

"""1行目
2行目
3行目""".splitlines()

difflib.unified_diff()の最初の2つの引数に、比較するテキストのリストやタプルをそれぞれ入れると、戻り値としてジェネレータが得られ、これをfor文でループさせると結果を行単位で処理できる。

for line in difflib.unified_diff([入力1], [入力2]):
    print(line)

3番目からの引数では差分の先頭のファイル名やタイムスタンプの部分に入る文字列を指定できるが、必須ではない。

使用例

diffの差分形式比較 (unified, context, side-by-side, デフォルト, ed)” のデータを用いて、difflib.unified_diff()を用いて比較してみることにする。

文字列のデータはスクリプトの中に記述している。

タイムスタンプ部分については、実際のタイムスタンプ値を用いたときの文字列への変換の参考として、適当な値を入れて表示している。

[任意]ファイル名:unifieddifftest.py
#! /usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import print_function

import difflib
import time


def main():
    text1 = """あいうえお
かきくけこ
さしすせそ
たちつてと
なにぬねの
はひふへほ
まみむめも
やゆよ
らりるれろ
わをん
がぎぐげご
ざじずぜぞ
だぢづでど
ばびぶべぼ
ぱぴぷぺぽ
アイウエオ
カキクケコ
サシスセソ
タチツテト
ナニヌネノ
ハヒフヘホ
マミムメモ
ヤユヨ
ラリルレロ
ワヲン
ガギグゲゴ
ザジズゼゾ
ダヂヅデド
バビブベボ
パピプペポ"""
    text2 = """あいうえお
かきくけこ
さしすせそ
たちつてと
なにぬねの
はひふへほ
まみむめも
やゆよら
りるれろ
わをん
ざじずぜぞ
だぢづでど
ばびぶべぼ
ぱぴぷぺぽ
アイウエオ
カキクケコ
サシスセソ
タチツテト
ナニヌネノ
ハヒフヘホ
マミムメモ
ヤユヨ
ラリルレロ
ワヲン
ァィゥェォ
ガギグゲゴ
ザジズゼゾ
ダヂヅデド
バビブベボ
パピプペポ"""

    # 戻り値はジェネレータ型
    gen = difflib.unified_diff(
        # 文字列のリストやタプルの形で入力2つを指定
        text1.splitlines(), text2.splitlines(),
        # ファイル名部分2つを指定
        "name-version.orig/test.txt", "name-version/test.txt",
        # タイムスタンプ部分の文字列(1つ目のファイル)
        # 今回は適当なタイムスタンプをフォーマット付けした
        time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(1145141919)),
        # タイムスタンプ部分の文字列(2つ目のファイル)
        time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(1155680062)),
        # 変更部分周辺をどの程度含めるか(既定値は3)
        3,
        # 制御行の末尾に付ける文字列
        # CR+LFなdiffを書き出したいなら "\r" を記述する
        "")

    # for文でループさせる
    for line in gen:
        print(line)


if __name__ == "__main__":
    main()

これを実行すると、下のようにそれぞれのテキスト部分を比較した結果がunified形式で表示される。これはdiff -u [ファイル1] [ファイル2]のコマンド行を実行した結果とタイムスタンプ部分の表記の微妙な違いを除いて同一のものとなる。

出力結果
--- name-version.orig/test.txt  2006-04-16 07:58:39
+++ name-version/test.txt       2006-08-16 07:14:22
@@ -5,10 +5,9 @@
 なにぬねの
 はひふへほ
 まみむめも
-やゆよ
-らりるれろ
+やゆよら
+りるれろ
 わをん
-がぎぐげご
 ざじずぜぞ
 だぢづでど
 ばびぶべぼ
@@ -23,6 +22,7 @@
 ヤユヨ
 ラリルレロ
 ワヲン
+ァィゥェォ
 ガギグゲゴ
 ザジズゼゾ
 ダヂヅデド

3行目からの差分の部分については完全に同一だが、diffコマンドでオプション指定により挙動を変えたり、difflib.unified_diff()の7番目の引数を3以外にしたりすると両者の出力は違ったものとなる。