HtmlDiffでテキストの差分を表示

difflib(2.1以降)のHtmlDiff(2.4以降)を使うと、2つのテキストの差分を取得して、HTMLで簡単に出力できる。行単位だけでなく、文字単位の差分も明示してくれるので便利。

withステートメント(2.5以降)を使うと、こんな感じ。

from __future__ import with_statement
from difflib import HtmlDiff
df = HtmlDiff()
with open('filename1') as f: f1 = f.readlines()

with open('filename2') as f: f2 = f.readlines()

with open("diff.html", "w") as f:
    f.writelines(df.make_file(f1, f2))

あまりに簡単すぎて死ぬかと思った。

(追記)日本語対応について

よくわからないけど、日本語はあまり得意じゃないっぽい。次のようにする。

from __future__ import with_statement
from difflib import HtmlDiff
from codecs import open

df = HtmlDiff()
f1 = open('filename1', 'r', 'sjis').readlines()

f2 = open('filename2', 'r', 'sjis').readlines()

f = open("diff.html", "w", 'utf-8')
f.writelines(df.make_file(f1, f2))
f.close()

すると、こんなふうに怒られる。

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\home\bin\Python25\lib\difflib.py", line 1693, in make_file
    context=context,numlines=numlines))
  File "c:\home\bin\Python25\lib\difflib.py", line 1960, in make_table
    next_href[i],tolist[i]))
UnicodeEncodeError: 'ascii' codec can't encode character u'\u25a0' in position 165: ordinal not in range(128)

これは、difflibがcStringIOモジュールを使っているのが原因。cStringIOモジュールは(StringIOと違って)Unicodeを受け付けないのだ。

とりあえず凌ぐなら、difflib.pyの該当箇所をいじるといい。でもこんなことはしないほうがいいと思う。どういう対応がまっとうでしょうか。

difflib.py:1948
        import cStringIO
        s = cStringIO.StringIO()

        ↓
        import StringIO
        s = StringIO.StringIO()