"difflib" — 差分の計算を助ける
******************************

バージョン 2.1 で追加.

このモジュールは、シーケンスを比較するためのクラスや関数を提供していま
す。例えば、ファイルの差分を計算して、それを HTML や context diff,
unified diff などいろいろなフォーマットで出力するために、このモジュー
ルを利用することができます。ディレクトリやファイル群を比較するためには
、 "filecmp" モジュールも参照してください。

class difflib.SequenceMatcher

   This is a flexible class for comparing pairs of sequences of any
   type, so long as the sequence elements are *hashable*.  The basic
   algorithm predates, and is a little fancier than, an algorithm
   published in the late 1980’s by Ratcliff and Obershelp under the
   hyperbolic name 「gestalt pattern matching.」  The idea is to find
   the longest contiguous matching subsequence that contains no 「junk
   」 elements (the Ratcliff and Obershelp algorithm doesn’t address
   junk).  The same idea is then applied recursively to the pieces of
   the sequences to the left and to the right of the matching
   subsequence.  This does not yield minimal edit sequences, but does
   tend to yield matches that 「look right」 to people.

   **実行時間:** 基本的な Ratcliff-Obershelp アルゴリズムは、最悪の場
   合3乗、期待値で2乗となります。 "SequenceMatcher" オブジェクトでは、
   最悪のケースで2乗、期待値は比較されるシーケンス中に共通に現れる要素
   数に非常にややこしく依存しています。最良の場合は線形時間になります
   。

   **自動 junk ヒューリスティック:** "SequenceMatcher" は、シーケンス
   の特定の要素を自動的に junk として扱うヒューリスティックをサポート
   しています。このヒューリスティックは、各個要素がシーケンス内に何回
   現れるかを数えます。ある要素の重複数が (最初のものは除いて) 合計で
   シーケンスの 1% 以上になり、そのシーケンスが 200 要素以上なら、その
   要素は 「popular」 であるものとしてマークされ、シーケンスのマッチン
   グの目的からは junk として扱われます。このヒューリスティックは、
   "SequenceMatcher" の作成時に "autojunk" パラメタを "False" に設定す
   ることで無効化できます。

   バージョン 2.7.1 で追加: *autojunk* パラメータ。

class difflib.Differ

   テキスト行からなるシーケンスを比較するクラスです。人が読むことので
   きる差分を作成します。 Differ クラスは "SequenceMatcher" クラスを利
   用して、行からなるシーケンスを比較したり、(ほぼ)同一の行内の文字を
   比較したりします。

   "Differ" クラスによる差分の各行は、2文字のコードで始まります:

   +------------+---------------------------------------------+
   | コード     | 意味                                        |
   +============+=============================================+
   | "'- '"     | 行はシーケンス1にのみ存在する               |
   +------------+---------------------------------------------+
   | "'+ '"     | 行はシーケンス2にのみ存在する               |
   +------------+---------------------------------------------+
   | "'  '"     | 行は両方のシーケンスで同一                  |
   +------------+---------------------------------------------+
   | "'? '"     | 行は入力シーケンスのどちらにも存在しない    |
   +------------+---------------------------------------------+

   『"?"』 で始まる行は、行内のどこに差異が存在するかに注意を向けよう
   とします。その行は、入力されたシーケンスのどちらにも存在しません。
   シーケンスがタブ文字を含むとき、これらの行は判別しづらいものになる
   ことがあります。

class difflib.HtmlDiff

   このクラスは、二つのテキストを左右に並べて比較表示し、行間あるいは
   行内の変更点を強調表示するような HTML テーブル (またはテーブルの入
   った完全な HTML ファイル) を生成するために使います。テーブルは完全
   差分モード、コンテキスト差分モードのいずれでも生成できます。

   このクラスのコンストラクタは以下のようになっています:

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

      "HtmlDiff" のインスタンスを初期化します。

      *tabsize* はオプションのキーワード引数で、タブストップ幅を指定し
      ます。デフォルトは "8" です。

      *wrapcolumn* はオプションのキーワード引数で、テキストを折り返す
      カラム幅を指定します。デフォルトは "None" で折り返しを行いません
      。

      *linejunk* および *charjunk* はオプションのキーワード引数で、
      "ndiff()"  ("HtmlDiff" はこの関数を使って左右のテキストの差分を
      HTML で生成します) に渡されます。それぞれの引数のデフォルト値お
      よび説明は "ndiff()" のドキュメントを参照してください。

   以下のメソッドが public になっています:

   make_file(fromlines, tolines [, fromdesc][, todesc][, context][, numlines])

      *fromlines* と *tolines* (いずれも文字列のリスト) を比較し、行間
      または行内の変更点が強調表示された行差分の入った表を持つ完全な
      HTML ファイルを文字列で返します。

      *fromdesc* および *todesc* はオプションのキーワード引数で、差分
      表示テーブルにおけるそれぞれ差分元、差分先ファイルのカラムのヘッ
      ダになる文字列を指定します (いずれもデフォルト値は空文字列です)
      。

      *context* および *numlines* はともにオプションのキーワード引数で
      す。*context* を "True" にするとコンテキスト差分を表示し、デフォ
      ルトの "False" にすると完全なファイル差分を表示します。
      *numlines* のデフォルト値は "5" で、*context* が "True" の場合、
      *numlines* は強調部分の前後にあるコンテキスト行の数を制御します
      。*context* が "False" の場合、*numlines* は 「next」 と書かれた
      ハイパーリンクをたどった時に到達する場所が次の変更部分より何行前
      にあるかを制御します (値をゼロにした場合、」next」 ハイパーリン
      クを辿ると変更部分の強調表示がブラウザの最上部に表示されるように
      なります)。

   make_table(fromlines, tolines [, fromdesc][, todesc][, context][, numlines])

      *fromlines* と *tolines* (いずれも文字列のリスト) を比較し、行間
      または行内の変更点が強調表示された行差分の入った完全な HTML テー
      ブルを文字列で返します。

      このメソッドの引数は、 "make_file()" メソッドの引数と同じです。

   "Tools/scripts/diff.py" はこのクラスへのコマンドラインフロントエン
   ドで、使い方を学ぶ上で格好の例題が入っています。

   バージョン 2.4 で追加.

difflib.context_diff(a, b[, fromfile][, tofile][, fromfiledate][, tofiledate][, n][, lineterm])

   *a* と *b* (文字列のリスト) を比較し、差分 (差分形式の行を生成する
   ジェネレータ(*generator*)) を、 context diff のフォーマット(以下「
   コンテクスト形式」)で返します。

   コンテクスト形式は、変更があった行に前後数行を加えてある、コンパク
   トな表現方法です。変更箇所は、変更前/変更後に分けて表します。コンテ
   クスト (変更箇所前後の行) の行数は *n* で指定し、デフォルト値は 3
   です。

   By default, the diff control lines (those with "***" or "---") are
   created with a trailing newline.  This is helpful so that inputs
   created from "file.readlines()" result in diffs that are suitable
   for use with "file.writelines()" since both the inputs and outputs
   have trailing newlines.

   行末に改行文字を持たない入力に対しては、出力でも改行文字を付加しな
   いように *lineterm* 引数に """" を渡してください。

   コンテクスト形式は、通常、ヘッダにファイル名と変更時刻を持っていま
   す。この情報は、文字列 *fromfile*, *tofile*, *fromfiledate*,
   *tofiledate* で指定できます。変更時刻の書式は、通常、ISO 8601 フォ
   ーマットで表されます。指定しなかった場合のデフォルト値は、空文字列
   です。

   >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n']
   >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n']
   >>> for line in context_diff(s1, s2, fromfile='before.py', tofile='after.py'):
   ...     sys.stdout.write(line)  # doctest: +NORMALIZE_WHITESPACE
   *** before.py
   --- after.py
   ***************
   *** 1,4 ****
   ! bacon
   ! eggs
   ! ham
     guido
   --- 1,4 ----
   ! python
   ! eggy
   ! hamster
     guido

   より詳細な例は、 difflib のコマンドラインインタフェース を参照して
   ください。

   バージョン 2.3 で追加.

difflib.get_close_matches(word, possibilities[, n][, cutoff])

   「十分」なマッチの上位のリストを返します。*word* はマッチさせたいシ
   ーケンス (大概は文字列) です。*possibilities* は *word* にマッチさ
   せるシーケンスのリスト (大概は文字列のリスト) です。

   オプションの引数 *n* (デフォルトでは "3")はメソッドの返すマッチの最
   大数です。*n* は "0" より大きくなければなりません。

   オプションの引数 *cutoff* (デフォルトでは "0.6")は、区間 [0, 1] に
   入る小数の値です。*word* との一致率がそれ未満の *possibilities* の
   要素は無視されます。

   *possibilities* の要素でマッチした上位(多くても *n* 個)は、類似度の
   スコアに応じて(一番似たものを先頭に)ソートされたリストとして返され
   ます。

   >>> get_close_matches('appel', ['ape', 'apple', 'peach', 'puppy'])
   ['apple', 'ape']
   >>> import keyword
   >>> get_close_matches('wheel', keyword.kwlist)
   ['while']
   >>> get_close_matches('apple', keyword.kwlist)
   []
   >>> get_close_matches('accept', keyword.kwlist)
   ['except']

difflib.ndiff(a, b[, linejunk][, charjunk])

   *a* と *b* (文字列のリスト) を比較し、差分 (差分形式の行を生成する
   ジェネレータ(*generator*)) を、 "Differ" のスタイルで返します。

   オプションのキーワードパラメータ *linejunk* と *charjunk* は、フィ
   ルタ関数を渡します (使わないときは "None"):

   *linejunk*: A function that accepts a single string argument, and
   returns true if the string is junk, or false if not. The default is
   ("None"), starting with Python 2.3.  Before then, the default was
   the module-level function "IS_LINE_JUNK()", which filters out lines
   without visible characters, except for at most one pound character
   ("'#'"). As of Python 2.3, the underlying "SequenceMatcher" class
   does a dynamic analysis of which lines are so frequent as to
   constitute noise, and this usually works better than the pre-2.3
   default.

   *charjunk*: A function that accepts a character (a string of length
   1), and returns if the character is junk, or false if not. The
   default is module-level function "IS_CHARACTER_JUNK()", which
   filters out whitespace characters (a blank or tab; note: bad idea
   to include newline in this!).

   "Tools/scripts/ndiff.py" は、この関数のコマンドラインのフロントエン
   ド（インターフェイス）です。

   >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1),
   ...              'ore\ntree\nemu\n'.splitlines(1))
   >>> print ''.join(diff),
   - one
   ?  ^
   + ore
   ?  ^
   - two
   - three
   ?  -
   + tree
   + emu

difflib.restore(sequence, which)

   差分を生成した元の二つのシーケンスのうち一つを返します。

   "Differ.compare()" または "ndiff()" によって生成された *sequence*
   を与えられると、行頭のプレフィクスを取りのぞいてファイル 1 または 2
   (引数 *which* で指定される) に由来する行を復元します。

   例:

   >>> diff = ndiff('one\ntwo\nthree\n'.splitlines(1),
   ...              'ore\ntree\nemu\n'.splitlines(1))
   >>> diff = list(diff) # materialize the generated delta into a list
   >>> print ''.join(restore(diff, 1)),
   one
   two
   three
   >>> print ''.join(restore(diff, 2)),
   ore
   tree
   emu

difflib.unified_diff(a, b[, fromfile][, tofile][, fromfiledate][, tofiledate][, n][, lineterm])

   *a* と *b* (文字列のリスト) を比較し、差分 (差分形式の行を生成する
   ジェネレータ(*generator*)) を、 unified diff フォーマット(以下「ユ
   ニファイド形式」)で返します。

   ユニファイド形式は変更があった行にコンテキストとなる前後数行を加え
   た、コンパクトな表現方法です。変更箇所は (変更前/変更後を分離したブ
   ロックではなく) インラインスタイルで表されます。コンテクストの行数
   は、*n* で指定し、デフォルト値は 3 です。

   By default, the diff control lines (those with "---", "+++", or
   "@@") are created with a trailing newline.  This is helpful so that
   inputs created from "file.readlines()" result in diffs that are
   suitable for use with "file.writelines()" since both the inputs and
   outputs have trailing newlines.

   行末に改行文字を持たない入力に対しては、出力でも改行文字を付加しな
   いように *lineterm* 引数に """" を渡してください。

   コンテクスト形式は、通常、ヘッダにファイル名と変更時刻を持っていま
   す。この情報は、文字列 *fromfile*, *tofile*, *fromfiledate*,
   *tofiledate* で指定できます。変更時刻の書式は、通常、ISO 8601 フォ
   ーマットで表されます。指定しなかった場合のデフォルト値は、空文字列
   です。

   >>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n']
   >>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n']
   >>> for line in unified_diff(s1, s2, fromfile='before.py', tofile='after.py'):
   ...     sys.stdout.write(line)   # doctest: +NORMALIZE_WHITESPACE
   --- before.py
   +++ after.py
   @@ -1,4 +1,4 @@
   -bacon
   -eggs
   -ham
   +python
   +eggy
   +hamster
    guido

   より詳細な例は、 difflib のコマンドラインインタフェース を参照して
   ください。

   バージョン 2.3 で追加.

difflib.IS_LINE_JUNK(line)

   Return true for ignorable lines.  The line *line* is ignorable if
   *line* is blank or contains a single "'#'", otherwise it is not
   ignorable.  Used as a default for parameter *linejunk* in "ndiff()"
   before Python 2.3.

difflib.IS_CHARACTER_JUNK(ch)

   無視できる文字のとき真を返します。文字 *ch* が空白、またはタブ文字
   のときには無視できます。それ以外の時には無視できません。 "ndiff()"
   の引数 *charjunk* としてデフォルトで使用されます。

参考:

  Pattern Matching: The Gestalt Approach
     John W. Ratcliff と D. E. Metzener による類似のアルゴリズムに関す
     る議論。Dr. Dobb’s Journal 1988年7月号掲載。


SequenceMatcherオブジェクト
===========================

"SequenceMatcher" クラスには、以下のようなコンストラクタがあります:

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

   オプションの引数 *isjunk* は、"None" (デフォルトの値です) にするか
   、単一の引数をとる関数でなければなりません。後者の場合、関数はシー
   ケンスの要素を受け取り、要素が junk であり、無視すべきである場合に
   限り真を返すようにしなければなりません。*isjunk* に "None" を渡すと
   、"lambda x: 0" を渡したのと同じになります; すなわち、いかなる要素
   も無視しなくなります。例えば以下のような引数を渡すと:

      lambda x: x in " \t"

   空白とタブ文字を無視して文字のシーケンスを比較します。

   オプションの引数 *a* と *b* は、比較される文字列で、デフォルトでは
   空の文字列です。両方のシーケンスの要素は、ハッシュ化可能
   (*hashable*)である必要があります。

   オプションの引数 *autojunk* は、自動 junk ヒューリスティックを無効
   にするために使えます。

   バージョン 2.7.1 で追加: *autojunk* パラメータ。

   "SequenceMatcher" オブジェクトは以下のメソッドを持ちます:

   set_seqs(a, b)

      比較される2つの文字列を設定します。

   "SequenceMatcher" オブジェクトは、2つ目のシーケンスについての詳細な
   情報を計算し、キャッシュします。 1つのシーケンスをいくつものシーケ
   ンスと比較する場合、まず "set_seq2()" を使って文字列を設定しておき
   、別の文字列を1つずつ比較するために、繰り返し "set_seq1()" を呼び出
   します。

   set_seq1(a)

      比較を行う1つ目のシーケンスを設定します。比較される2つ目のシーケ
      ンスは変更されません。

   set_seq2(b)

      比較を行う2つ目のシーケンスを設定します。比較される1つ目のシーケ
      ンスは変更されません。

   find_longest_match(alo, ahi, blo, bhi)

      "a[alo:ahi]" と "b[blo:bhi]" の中から、最長のマッチ列を探します
      。

      *isjunk* が省略されたか "None" の時、 "find_longest_match()" は
      "a[i:i+k]" が "b[j:j+k]" と等しいような "(i, 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 要素にマッチするよう、できる限り広げていきます。そのため結
      果となる列は、探している列のたまたま直前にあった同一の junk 以外
      の junk にはマッチしません。

      以下は前と同じサンプルですが、空白を junk とみなしています。これ
      は "' abcd'" が2つ目の列の末尾にある "' abcd'" にマッチしないよ
      うにしています。代わりに "'abcd'" にはマッチします。そして 2つ目
      の文字列中、一番左の "'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)" を返します。

      バージョン 2.6 で変更: このメソッドは *named tuple* "Match(a, b,
      size)" を返します。

   get_matching_blocks()

      マッチしたシーケンス中で個別にマッチしたシーケンスをあらわす、3
      つの値のリストを返します。それぞれの値は "(i, j, n)" という形式
      であらわされ、"a[i:i+n] == b[j:j+n]" という関係を意味します。3つ
      の値は *i* と *j* の間で単調に増加します。

      最後のタプルはダミーで、"(len(a), len(b), 0)" という値を持ちます
      。これは "n == 0" である唯一のタプルです。もし "(i, j, n)" と
      "(i', j', n')" がリストで並んでいる3つ組で、2つ目が最後の3つ組で
      なければ、"i+n != i'" または "j+n != j'" です。言い換えると並ん
      でいる3つ組は常に隣接していない同じブロックを表しています。

      バージョン 2.5 で変更: The guarantee that adjacent triples
      always describe non-adjacent blocks was implemented.

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

   get_opcodes()

      *a* を *b* にするための方法を記述する5つのタプルを返します。それ
      ぞれのタプルは "(tag, i1, i2, j1, j2)" という形式であらわされま
      す。最初のタプルは "i1 == j1 == 0" であり、*i1* はその前にあるタ
      プルの *i2* と同じ値です。同様に *j1* は前の *j2* と同じ値になり
      ます。

      *tag* の値は文字列であり、次のような意味です:

      +-----------------+-----------------------------------------------+
      | 値              | 意味                                          |
      +=================+===============================================+
      | "'replace'"     | "a[i1:i2]" は "b[j1:j2]" に置き換えられる。   |
      +-----------------+-----------------------------------------------+
      | "'delete'"      | "a[i1:i2]" は削除される。この時、"j1 == j2"   |
      |                 | である。                                      |
      +-----------------+-----------------------------------------------+
      | "'insert'"      | "b[j1:j2]" が "a[i1:i1]" に挿入される。この時 |
      |                 | "i1 == i2" である 。                          |
      +-----------------+-----------------------------------------------+
      | "'equal'"       | "a[i1:i2] == b[j1:j2]" (サブシーケンスは等し  |
      |                 | い).                                          |
      +-----------------+-----------------------------------------------+

      For example:

      >>> a = "qabxcd"
      >>> b = "abycdf"
      >>> s = SequenceMatcher(None, a, b)
      >>> for tag, i1, i2, j1, j2 in s.get_opcodes():
      ...    print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" %
      ...           (tag, i1, i2, a[i1:i2], j1, j2, b[j1:j2]))
       delete a[0:1] (q) b[0:0] ()
        equal a[1:3] (ab) b[0:2] (ab)
      replace a[3:4] (x) b[2:3] (y)
        equal a[4:6] (cd) b[3:5] (cd)
       insert a[6:6] () b[5:6] (f)

   get_grouped_opcodes([n])

      最大 *n* 行までのコンテクストを含むグループを生成するような、ジ
      ェネレータ(*generator*)を返します。

      このメソッドは、 "get_opcodes()" で返されるグループの中から、似
      たような差異のかたまりに分け、間に挟まっている変更の無い部分を省
      きます。

      グループは "get_opcodes()" と同じ書式で返されます。

      バージョン 2.3 で追加.

   ratio()

      [0, 1] の範囲の浮動小数点数で、シーケンスの類似度を測る値を返し
      ます。

      T が2つのシーケンスの要素数の総計だと仮定し、M をマッチした数と
      すると、この値は 2.0*M / T であらわされます。もしシーケンスがま
      ったく同じ場合、値は "1.0" となり、まったく異なる場合には "0.0"
      となります。

      このメソッドは "get_matching_blocks()" または "get_opcodes()" が
      まだ呼び出されていない場合には非常にコストが高いです。この場合、
      上限を素早く計算するために、 "quick_ratio()" もしくは
      "real_quick_ratio()" を最初に試してみる方がいいかもしれません。

   quick_ratio()

      "ratio()" の上界を、より高速に計算します。

   real_quick_ratio()

      "ratio()" の上界を、非常に高速に計算します。

この文字列全体のマッチ率を返す3つのメソッドは、精度の異なる近似値を返
します。 "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


SequenceMatcher の例
====================

This example compares two strings, considering blanks to be 「junk:」

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

"ratio()" は、[0, 1] の範囲の値を返し、シーケンスの類似度を測ります。
経験によると、 "ratio()" の値が0.6を超えると、シーケンスがよく似ている
ことを示します:

>>> 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" となる唯一のケースです。

はじめのシーケンスがどのようにして2番目のものになるのかを知るには、
"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]

参考:

  * "SequenceMatcher" を使った、シンプルで使えるコードを知るには、こ
    の モジュールの関数 "get_close_matches()" を参照してください。

  * Simple version control recipe "SequenceMatcher" で作った小規模ア
    プ リケーション。


Differ オブジェクト
===================

"Differ" オブジェクトによって生成された差分が **最小** であるなどとは
言いません。むしろ、最小の差分はしばしば直観に反しています。その理由は
、どこでもできるとなれば一致を見いだしてしまうからで、ときには思いがけ
なく100ページも離れたマッチになってしまうのです。一致点を互いに隣接し
たマッチに制限することで、場合によって長めの差分を出力するというコスト
を掛けることにはなっても、ある種の局所性を保つことができるのです。

"Differ" は、以下のようなコンストラクタを持ちます:

class difflib.Differ([linejunk[, charjunk]])

   オプションのキーワードパラメータ *linejunk* と *charjunk* は、フィ
   ルタ関数を渡します (使わないときは "None"):

   *linejunk*: ひとつの文字列引数を受け取る関数です。文字列が junk の
   ときに真を返します。デフォルトでは、"None" であり、どんな行であって
   も junk とは見なされません。

   *charjunk*: この関数は文字(長さ1の文字列)を引数として受け取り、文字
   が junk であるときに真を返します。デフォルトは "None" であり、どん
   な文字も junk とは見なされません。

   "Differ" オブジェクトは、以下の1つのメソッドを通して利用されます。
   （差分を生成します）:

   compare(a, b)

      文字列からなる2つのシーケンスを比較し、差分（を表す文字列からな
      るシーケンス）を生成します。

      Each sequence must contain individual single-line strings ending
      with newlines.  Such sequences can be obtained from the
      "readlines()" method of file-like objects.  The delta generated
      also consists of newline-terminated strings, ready to be printed
      as-is via the "writelines()" method of a file-like object.


Differ の例
===========

This example compares two texts. First we set up the texts, sequences
of individual single-line strings ending with newlines (such sequences
can also be obtained from the "readlines()" method of file-like
objects):

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

次に Differ オブジェクトをインスタンス化します:

>>> d = Differ()

注意:  "Differ" オブジェクトをインスタンス化するとき、行 junk と文字
junk をフィルタリングする関数を渡すことができます。詳細は "Differ()"
コンストラクタを参照してください。

最後に、2つを比較します:

>>> 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 のコマンドラインインタフェース
======================================

この例は、 difflib を使って "diff" に似たユーティリティーを作成する方
法を示します。これは、 Python のソース配布物にも、
"Tools/scripts/diff.py" として含まれています。

   """ 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, time, difflib, optparse

   def main():
        # Configure the option parser
       usage = "usage: %prog [options] fromfile tofile"
       parser = optparse.OptionParser(usage)
       parser.add_option("-c", action="store_true", default=False,
                         help='Produce a context format diff (default)')
       parser.add_option("-u", action="store_true", default=False,
                         help='Produce a unified format diff')
       hlp = 'Produce HTML side by side diff (can use -c and -l in conjunction)'
       parser.add_option("-m", action="store_true", default=False, help=hlp)
       parser.add_option("-n", action="store_true", default=False,
                         help='Produce a ndiff format diff')
       parser.add_option("-l", "--lines", type="int", default=3,
                         help='Set number of context lines (default 3)')
       (options, args) = parser.parse_args()

       if len(args) == 0:
           parser.print_help()
           sys.exit(1)
       if len(args) != 2:
           parser.error("need to specify both a fromfile and tofile")

       n = options.lines
       fromfile, tofile = args # as specified in the usage string

       # we're passing these as arguments to the diff function
       fromdate = time.ctime(os.stat(fromfile).st_mtime)
       todate = time.ctime(os.stat(tofile).st_mtime)
       fromlines = open(fromfile, 'U').readlines()
       tolines = open(tofile, 'U').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)

       # we're using writelines because diff is a generator
       sys.stdout.writelines(diff)

   if __name__ == '__main__':
       main()
