difflib ---Delta计算助手

源代码: Lib/difflib.py


此模块提供用于比较序列的类和函数。例如,它可以用于比较文件,并且可以生成各种格式的文件差异的信息,包括HTML和context以及统一的差异。有关目录和文件的比较,另请参见 filecmp 模块。

class difflib.SequenceMatcher

这是一个灵活的类,用于比较任何类型的序列对,只要序列元素是 hashable . 基本算法比Ratcliff和Obershelp在20世纪80年代后期发布的一种算法早一点,也比它稍差一点,它的双曲线名称是“格式塔模式匹配”。其思想是找到最长的连续匹配子序列,其中不包含“垃圾”元素;这些“垃圾”元素在某种意义上是无趣的。如空行或空白。(处理垃圾是Ratcliff和Obershelp算法的一个扩展。)同样的想法随后被递归地应用到匹配子序列左侧和右侧的序列片段上。这不会产生最小的编辑序列,但会产生对人“正确”的匹配。

时机: 基本的Ratcliff Obershelp算法是最坏情况下的三次时间和预期情况下的二次时间。 SequenceMatcher 是最坏情况下的二次时间,其预期情况行为以复杂的方式取决于序列共有多少个元素;最佳情况时间是线性的。

自动垃圾启发式: SequenceMatcher 支持自动将某些序列项视为垃圾的启发式方法。启发式计算每个项目在序列中出现的次数。如果一个项目的重复项(在第一个项目之后)占序列的1%以上,且序列长度至少为200个项目,则该项目标记为“热门”,并被视为垃圾,以便进行序列匹配。通过设置 autojunk 参数 False 当创建 SequenceMatcher .

3.2 新版功能: 这个 汽车垃圾 参数。

class difflib.Differ

这是一个用于比较文本行序列并产生人类可读差异或增量的类。不同用途 SequenceMatcher 两者都用来比较行的序列,以及比较相似(接近匹配)行中的字符序列。

A的每一行 Differ Delta以两个字母的代码开头:

代码

意义

'- '

序列1的唯一行

'+ '

序列2的唯一行

'  '

两个序列共用的行

'? '

两个输入序列中都不存在行

以' ? '试图引导眼睛了解行内差异,但这两个输入序列都不存在。如果序列包含制表符,这些行可能会混淆。

class difflib.HtmlDiff

此类可用于创建一个HTML表(或包含该表的完整HTML文件),显示文本的并排、逐行比较以及行间和行内更改突出显示。可以在完全或上下文差异模式下生成表。

此类的构造函数是:

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

初始化的实例 HtmlDiff .

表格大小 是用于指定制表位间距的可选关键字参数,默认为 8 .

封装柱 是一个可选关键字,用于指定断行和换行的列号,默认为 None 不换行的地方。

linejunkcharjunk 是否将可选关键字参数传递到 ndiff() (由使用) HtmlDiff 生成并排的HTML差异)。见 ndiff() 参数默认值和说明的文档。

以下方法是公开的:

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

比较 从线托林 (字符串列表)并返回一个字符串,该字符串是一个完整的HTML文件,其中包含一个显示逐行差异的表,其中突出显示行间和行内更改。

从德斯科托德斯克 是可选的关键字参数,用于指定从/到文件列标题字符串(均默认为空字符串)。

context数字 都是可选关键字参数。集合 contextTrue 如果要显示上下文差异,则默认为 False 显示完整的文件。 数字 默认为 5 .什么时候? contextTrue 数字 控制围绕差异突出显示的上下文行数。什么时候? contextFalse 数字 控制使用“下一个”超链接时在差异突出显示之前显示的行数(设置为零将导致“下一个”超链接将下一个差异突出显示放置在浏览器顶部,而不带任何前导上下文)。

注解

从德斯科托德斯克 被解释为未转义的HTML,在接收来自不受信任源的输入时应正确转义。

在 3.5 版更改: charset 只添加关键字参数。HTML文档的默认字符集从 'ISO-8859-1''utf-8' .

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

比较 从线托林 (字符串列表)并返回一个字符串,该字符串是一个完整的HTML表,显示逐行差异,突出显示行间和行内更改。

此方法的参数与 make_file() 方法。

Tools/scripts/diff.py 是这个类的命令行前端,包含一个很好的使用示例。

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

比较 ab (字符串列表);返回delta(a generator 以上下文diff格式生成delta行)。

上下文差异是一种紧凑的方式,只显示已更改的行和几行上下文。更改以“前/后”样式显示。上下文行数由 n 默认为3。

默认情况下,差异控制行(具有 ***--- )是用尾随新行创建的。这很有帮助,因此从中创建的输入 io.IOBase.readlines() 导致适合与一起使用的差异 io.IOBase.writelines() 因为输入和输出都有尾随的新行。

对于没有尾随新行的输入,设置 直线加速器 参数 "" 这样,输出将一致地不换行。

上下文diff格式通常具有文件名和修改时间的标题。任何或所有这些可以使用字符串指定 来自文件托菲尔从文件中提取吃喝 . 修改时间通常以ISO 8601格式表示。如果未指定,字符串默认为空白。

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

difflib的命令行接口 更详细的例子。

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

返回最佳“足够好”匹配的列表。 word 是需要紧密匹配的序列(通常是字符串),并且 可能性 是要匹配的序列列表 word (通常是字符串列表)。

可选参数 n (默认) 3 )是要返回的最大匹配数; n 必须大于 0 .

可选参数 截止 (默认) 0.6 )是一个浮动范围 [0, 1] . 不得分至少与 word 被忽略。

最好的(不超过 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)

比较 ab (字符串列表);返回 Differ -样式Delta(A generator 生成三角线)。

可选关键字参数 linejunkcharjunk 是过滤功能(或 None ):

linejunk :接受单个字符串参数的函数,如果字符串是垃圾邮件,则返回“真”,否则返回“假”。默认值为 None . 还有一个模块级功能 IS_LINE_JUNK() ,它过滤掉没有可见字符的行,但最多只能过滤一磅字符。 ('#' )--然而,潜在的 SequenceMatcher 类对哪些行频繁地构成噪声进行动态分析,这通常比使用此函数效果更好。

charjunk :接受一个字符(长度为1的字符串)并返回该字符是否为垃圾邮件的函数,否则返回false。默认为模块级功能 IS_CHARACTER_JUNK() ,它过滤掉空白字符(空白或制表符;在其中包含换行符是个坏主意!).

Tools/scripts/ndiff.py 是此功能的命令行前端。

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

返回生成delta的两个序列之一。

给出了一个 序列 生产的 Differ.compare()ndiff() ,从文件1或2提取行(参数 哪一个 )删除行前缀。

例子:

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

比较 ab (字符串列表);返回delta(a generator 以统一的diff格式生成delta行)。

统一的差异是一种简洁的方式,只显示已更改的行和几行上下文。更改以内联样式显示(而不是单独的前/后块)。上下文行数由 n 默认为3。

默认情况下,差异控制行(具有 ---+++@@ )是用尾随新行创建的。这很有帮助,因此从中创建的输入 io.IOBase.readlines() 导致适合与一起使用的差异 io.IOBase.writelines() 因为输入和输出都有尾随的新行。

对于没有尾随新行的输入,设置 直线加速器 参数 "" 这样,输出将一致地不换行。

上下文diff格式通常具有文件名和修改时间的标题。任何或所有这些可以使用字符串指定 来自文件托菲尔从文件中提取吃喝 . 修改时间通常以ISO 8601格式表示。如果未指定,字符串默认为空白。

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

difflib的命令行接口 更详细的例子。

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

比较 ab (字节对象列表)使用 DFUNC ;以返回的格式生成一个增量行序列(也包括字节)。 DFUNC . DFUNC 必须是可调用的,通常为 unified_diff()context_diff() .

允许您比较具有未知或不一致编码的数据。所有输入,除了 n 必须是字节对象,而不是通过无损转换所有输入(除了 n )到str,然后调用 dfunc(a, b, fromfile, tofile, fromfiledate, tofiledate, n, lineterm) .的输出 DFUNC 然后转换回字节,因此接收到的增量行具有相同的未知/不一致编码 ab .

3.5 新版功能.

difflib.IS_LINE_JUNK(line)

返回 True 为了那些不光彩的台词。线路 line 如果是可忽略的 line 为空或包含单个 '#' ,否则它是不可忽略的。用作参数的默认值 垃圾车 在里面 ndiff() 在旧版本中。

difflib.IS_CHARACTER_JUNK(ch)

返回 True 对于可耻的角色。角色 ch 如果是可忽略的 ch 是空格或制表符,否则不可忽略。用作参数的默认值 船坞 在里面 ndiff() .

参见

Pattern Matching: The Gestalt Approach

约翰·W·拉特克里夫和D·E·梅特泽纳对类似算法的讨论。这是在 Dr. Dobb's Journal 在1988年7月。

SequenceMatcher对象

这个 SequenceMatcher 类具有此构造函数:

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

可选参数 垃圾 必须是 None (默认值)或一个单参数函数,它接受一个序列元素,并且仅当该元素是“垃圾”且应被忽略时才返回true。经过 None 对于 垃圾 相当于通过 lambda x: False ;换句话说,不忽略任何元素。例如,通过:

lambda x: x in " \t"

如果您将行作为字符序列进行比较,并且不希望在空白或硬制表符上进行同步。

可选参数 ab 是要比较的序列;都默认为空字符串。两个序列的元素必须 hashable .

可选参数 汽车垃圾 可用于禁用自动垃圾启发式。

3.2 新版功能: 这个 汽车垃圾 参数。

SequenceMatcher对象获得三个数据属性: B废料 是的元素集 b 为此 垃圾True不受欢迎的 是启发式(如果未禁用)认为流行的一组非垃圾元素; b2j 是dict映射的其余元素 b 它们出现的位置列表。这三个都会在任何时候重置 b 重置为 set_seqs()set_seq2() .

3.2 新版功能: 这个 B废料不受欢迎的 属性。

SequenceMatcher 对象具有以下方法:

set_seqs(a, b)

设置要比较的两个序列。

SequenceMatcher 计算并缓存有关第二个序列的详细信息,因此如果要将一个序列与多个序列进行比较,请使用 set_seq2() 一次设置常用序列并调用 set_seq1() 对于其他序列,重复一次。

set_seq1(a)

设置要比较的第一个序列。要比较的第二个序列没有改变。

set_seq2(b)

设置要比较的第二个序列。第一个要比较的序列没有改变。

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

在中查找最长匹配块 a[alo:ahi]b[blo:bhi] .

如果 垃圾 被省略或 Nonefind_longest_match() 返回 (i, j, k) 这样的话 a[i:i+k] 等于 b[j:j+k] 在哪里 alo <= i <= i+k <= ahiblo <= 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)

如果 垃圾 如果提供了,则首先按上述方式确定最长的匹配块,但有一个附加限制,即块中不显示垃圾元素。然后,通过匹配(仅)两侧的垃圾元素,尽可能扩展该块。因此,结果块永远不会在垃圾上匹配,除非相同的垃圾恰好与一个有趣的匹配相邻。

这里有一个和以前一样的例子,但是考虑到空白是垃圾。防止 ' abcd' 从匹配 ' abcd' 在第二个序列的尾部。相反,只有 'abcd' 可以匹配,最左边的匹配 '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) .

此方法返回 named tuple Match(a, b, size) .

在 3.9 版更改: 添加了默认参数。

get_matching_blocks()

返回描述非重叠匹配子序列的三元组列表。每三个都是这样的 (i, j, n) 也就是说 a[i:i+n] == b[j:j+n] . 三重态单调递增 ij .

最后三个是一个虚拟的,并且具有值 (len(a), len(b), 0) . 它是唯一一个有 n == 0 . 如果 (i, j, n)(i', j', n') 是列表中相邻的三个,第二个不是列表中的最后三个,然后 i+n < i'j+n < j' 换言之,相邻的三元组总是描述非相邻的相等块。

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

返回描述如何转换的5元组列表 a 进入之内 b . 每个元组的形式都是 (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] (子序列相等)。

例如::

>>> a = "qabxcd"
>>> b = "abycdf"
>>> s = SequenceMatcher(None, a, b)
>>> for tag, i1, i2, j1, j2 in s.get_opcodes():
...     print('{:7}   a[{}:{}] --> b[{}:{}] {!r:>8} --> {!r}'.format(
...         tag, i1, i2, j1, j2, a[i1:i2], b[j1:j2]))
delete    a[0:1] --> b[0:0]      'q' --> ''
equal     a[1:3] --> b[0:2]     'ab' --> 'ab'
replace   a[3:4] --> b[2:3]      'x' --> 'y'
equal     a[4:6] --> b[3:5]     'cd' --> 'cd'
insert    a[6:6] --> b[5:6]       '' --> 'f'
get_grouped_opcodes(n=3)

返回A generator 最多的组 n 上下文行。

从返回的组开始 get_opcodes() 该方法分离出较小的变化簇,消除了没有变化的干预范围。

这些组的返回格式与 get_opcodes() .

ratio()

返回序列相似性的度量值,作为范围内的浮点值 [0, 1] .

其中t是两个序列中元素的总数,m是匹配的数目,这是2.0 * M/T.注意这是 1.0 如果序列相同,并且 0.0 如果他们没有共同点。

这是昂贵的计算如果 get_matching_blocks()get_opcodes() 还没有被调用,在这种情况下,您可能想尝试 quick_ratio()real_quick_ratio() 首先得到上界。

注解

警告:结果是 ratio() 调用可能取决于参数的顺序。例如::

>>> SequenceMatcher(None, 'tide', 'diet').ratio()
0.25
>>> SequenceMatcher(None, 'diet', 'tide').ratio()
0.5
quick_ratio()

返回上的上界 ratio() 比较快。

real_quick_ratio()

返回上的上界 ratio() 很快。

返回匹配与字符总数之比的三种方法由于近似程度不同而产生不同的结果,尽管 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示例

此示例比较两个字符串,将空格视为“垃圾”:

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

ratio() 返回float-in [0, 1] ,测量序列的相似性。根据经验,a 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) ,这是最后一个tuple元素(匹配的元素数)是 0 .

如果要知道如何将第一个序列更改为第二个序列,请使用 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]

参见

不同对象

注意 Differ -生成的增量不要求 极小值 差异。相反,最小的差异通常是反直觉的,因为它们可以在任何可能的地方同步,有时会意外地匹配100页。将同步点限制为连续匹配保留了局部性的一些概念,偶尔会产生较长的差异。

这个 Differ 类具有此构造函数:

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

可选关键字参数 linejunkcharjunk 用于过滤功能(或 None ):

linejunk :接受单个字符串参数的函数,如果字符串是垃圾邮件,则返回true。默认值为 None 也就是说没有一行被认为是垃圾。

charjunk :接受单个字符参数(长度为1的字符串)的函数,如果字符是垃圾邮件,则返回true。默认值为 None 也就是说没有任何角色被认为是垃圾。

这些垃圾过滤功能可以加快匹配速度以发现差异,并且不会导致忽略任何不同的行或字符。请阅读 find_longest_match() 方法的 垃圾 用于解释的参数。

Differ 通过单个方法使用对象(生成增量):

compare(a, b)

比较两个行序列,并生成delta(一个行序列)。

每个序列必须包含以换行符结尾的单个单行字符串。这样的序列可以从 readlines() 类文件对象的方法。生成的增量还包括以换行符结尾的字符串,可以通过 writelines() 类文件对象的方法。

不同的例子

此示例比较两个文本。首先,我们设置文本、以换行符结尾的单个单行字符串的序列(这样的序列也可以从 readlines() 文件类对象的方法):

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

接下来我们实例化一个不同的对象:

>>> d = Differ()

请注意,在实例化 Differ 对象我们可以传递函数来过滤行和字符“垃圾”。请参见 Differ() 有关详细信息的构造函数。

最后,我们比较了这两种情况:

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

result 是一个字符串列表,所以让我们漂亮地打印它:

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

#!/usr/bin/env python3
""" 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, difflib, argparse
from datetime import datetime, timezone

def file_mtime(path):
    t = datetime.fromtimestamp(os.stat(path).st_mtime,
                               timezone.utc)
    return t.astimezone().isoformat()

def main():

    parser = argparse.ArgumentParser()
    parser.add_argument('-c', action='store_true', default=False,
                        help='Produce a context format diff (default)')
    parser.add_argument('-u', action='store_true', default=False,
                        help='Produce a unified format diff')
    parser.add_argument('-m', action='store_true', default=False,
                        help='Produce HTML side by side diff '
                             '(can use -c and -l in conjunction)')
    parser.add_argument('-n', action='store_true', default=False,
                        help='Produce a ndiff format diff')
    parser.add_argument('-l', '--lines', type=int, default=3,
                        help='Set number of context lines (default 3)')
    parser.add_argument('fromfile')
    parser.add_argument('tofile')
    options = parser.parse_args()

    n = options.lines
    fromfile = options.fromfile
    tofile = options.tofile

    fromdate = file_mtime(fromfile)
    todate = file_mtime(tofile)
    with open(fromfile) as ff:
        fromlines = ff.readlines()
    with open(tofile) as tf:
        tolines = tf.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)

    sys.stdout.writelines(diff)

if __name__ == '__main__':
    main()