7.9. filecmp — 文件对比 | 文件系统 |《python 3 标准库实例教程》| python 技术论坛-金年会app官方网

未匹配的标注

目的:比较系统上的文件和文件夹.

filecmp 模块包含函数和类来比较系统上的文件和文件夹

示例数据

例子中所讨论的测试文件是由  filecmp_mkexamples.py 创建.

filecmp_mkexamples.py

import os
def mkfile(filename, body=none):
    with open(filename, 'w') as f:
        f.write(body or filename)
    return
def make_example_dir(top):
    if not os.path.exists(top):
        os.mkdir(top)
    curdir = os.getcwd()
    os.chdir(top)
    os.mkdir('dir1')
    os.mkdir('dir2')
    mkfile('dir1/file_only_in_dir1')
    mkfile('dir2/file_only_in_dir2')
    os.mkdir('dir1/dir_only_in_dir1')
    os.mkdir('dir2/dir_only_in_dir2')
    os.mkdir('dir1/common_dir')
    os.mkdir('dir2/common_dir')
    mkfile('dir1/common_file', 'this file is the same')
    mkfile('dir2/common_file', 'this file is the same')
    mkfile('dir1/not_the_same')
    mkfile('dir2/not_the_same')
    mkfile('dir1/file_in_dir1', 'this is a file in dir1')
    os.mkdir('dir2/file_in_dir1')
    os.chdir(curdir)
    return
if __name__ == '__main__':
    os.chdir(os.path.dirname(__file__) or os.getcwd())
    make_example_dir('example')
    make_example_dir('example/dir1/common_dir')
    make_example_dir('example/dir2/common_dir')

运行程序后会在 example 文件夹中创建以下文件:

$ find example | sort
example
example/dir1
example/dir1/common_dir
example/dir1/common_dir/dir1
example/dir1/common_dir/dir1/common_dir
example/dir1/common_dir/dir1/common_file
example/dir1/common_dir/dir1/dir_only_in_dir1
example/dir1/common_dir/dir1/file_in_dir1
example/dir1/common_dir/dir1/file_only_in_dir1
example/dir1/common_dir/dir1/not_the_same
example/dir1/common_dir/dir2
example/dir1/common_dir/dir2/common_dir
example/dir1/common_dir/dir2/common_file
example/dir1/common_dir/dir2/dir_only_in_dir2
example/dir1/common_dir/dir2/file_in_dir1
example/dir1/common_dir/dir2/file_only_in_dir2
example/dir1/common_dir/dir2/not_the_same
example/dir1/common_file
example/dir1/dir_only_in_dir1
example/dir1/file_in_dir1
example/dir1/file_only_in_dir1
example/dir1/not_the_same
example/dir2
example/dir2/common_dir
example/dir2/common_dir/dir1
example/dir2/common_dir/dir1/common_dir
example/dir2/common_dir/dir1/common_file
example/dir2/common_dir/dir1/dir_only_in_dir1
example/dir2/common_dir/dir1/file_in_dir1
example/dir2/common_dir/dir1/file_only_in_dir1
example/dir2/common_dir/dir1/not_the_same
example/dir2/common_dir/dir2
example/dir2/common_dir/dir2/common_dir
example/dir2/common_dir/dir2/common_file
example/dir2/common_dir/dir2/dir_only_in_dir2
example/dir2/common_dir/dir2/file_in_dir1
example/dir2/common_dir/dir2/file_only_in_dir2
example/dir2/common_dir/dir2/not_the_same
example/dir2/common_file
example/dir2/dir_only_in_dir2
example/dir2/file_in_dir1
example/dir2/file_only_in_dir2
example/dir2/not_the_same

common_dir 目录下重复一次相同的文件结构来提供递归选项

比较文件

cmp() 方法用于比较系统上两个文件。

filecmp_cmp.py

import filecmp
print('common_file :', end=' ')
print(filecmp.cmp('example/dir1/common_file',
                  'example/dir2/common_file'),
      end=' ')
print(filecmp.cmp('example/dir1/common_file',
                  'example/dir2/common_file',
                  shallow=false))
print('not_the_same:', end=' ')
print(filecmp.cmp('example/dir1/not_the_same',
                  'example/dir2/not_the_same'),
      end=' ')
print(filecmp.cmp('example/dir1/not_the_same',
                  'example/dir2/not_the_same',
                  shallow=false))
print('identical   :', end=' ')
print(filecmp.cmp('example/dir1/file_only_in_dir1',
                  'example/dir1/file_only_in_dir1'),
      end=' ')
print(filecmp.cmp('example/dir1/file_only_in_dir1',
                  'example/dir1/file_only_in_dir1',
                  shallow=false))

shallow 参数告诉 cmp() 除了对比文件元数据是否还要比较文件内容。默认只是比较从 os.stat() 获取到的文件元信息,如果 stat 相同那么文件就相同,因此同时创建并且大小相同的文件被认为是相同的,即使它们的内容不同。当 shallowfalse 的时候,文件内容也会参与比较。

$ python3 filecmp_cmp.py
common_file : true true
not_the_same: true false
identical   : true true

要在不递归的情况下比较两个目录中的一组文件,请使用 cmpfiles() 。这个方法参数是两个目录名称以及要比较的公共文件列表。公共文件列表应该只包含文件名(如果包含目录会导致不匹配)而且文件必须在两个目录都存在。下面的例子展示了一个构建公共文件列表的简单方法。cmpfiles() 也可以传入一个 shallow 参数,意义同 cmp()

filecmp_cmpfiles.py

import filecmp
import os
# 构建公共文件列表
d1_contents = set(os.listdir('example/dir1'))
d2_contents = set(os.listdir('example/dir2'))
common = list(d1_contents & d2_contents)
common_files = [
    f
    for f in common
    if os.path.isfile(os.path.join('example/dir1', f))
]
print('common files:', common_files)
# 比较目录
match, mismatch, errors = filecmp.cmpfiles(
    'example/dir1',
    'example/dir2',
    common_files,
)
print('match       :', match)
print('mismatch    :', mismatch)
print('errors      :', errors)

cmpfiles() 方法返回三个文件名列表,依次为匹配的文件列表,不匹配的文件列表以及不能比较的(权限问题或者其他任何问题)。

$ python3 filecmp_cmpfiles.py
common files: ['not_the_same', 'file_in_dir1', 'common_file']
match       : ['not_the_same', 'common_file']
mismatch    : ['file_in_dir1']
errors      : []

目录比较

前面介绍的方法都只是适用于相对简单的比较。对于递归比较大目录树或者需要完整的分析的时候,dircmp() 方法通常比较有用。简单使用的时候,可以用 report() 方法打印比较报告。

filecmp_dircmp_report.py

import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
dc.report()

输出是文本形式的,仅仅展示了给定的两个目录比较结果而没有递归。
这个例子中 「not_the_same」被认为是相同的是因为没有比较内容。这里没有办法像 cmp() 那样比较文件内容。

$ python3 filecmp_dircmp_report.py
diff example/dir1 example/dir2
only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1']
only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2']
identical files : ['common_file', 'not_the_same']
common subdirectories : ['common_dir']
common funny cases : ['file_in_dir1']

要看更多详细的报告,使用 report_full_closure()

filecmp_dircmp_report_full_closure.py

import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
dc.report_full_closure()

输出包括所有子目录的比较。

$ python3 filecmp_dircmp_report_full_closure.py
diff example/dir1 example/dir2
only in example/dir1 : ['dir_only_in_dir1', 'file_only_in_dir1']
only in example/dir2 : ['dir_only_in_dir2', 'file_only_in_dir2']
identical files : ['common_file', 'not_the_same']
common subdirectories : ['common_dir']
common funny cases : ['file_in_dir1']
diff example/dir1/common_dir example/dir2/common_dir
common subdirectories : ['dir1', 'dir2']
diff example/dir1/common_dir/dir1 example/dir2/common_dir/dir1
identical files : ['common_file', 'file_in_dir1',
'file_only_in_dir1', 'not_the_same']
common subdirectories : ['common_dir', 'dir_only_in_dir1']
diff example/dir1/common_dir/dir1/dir_only_in_dir1
example/dir2/common_dir/dir1/dir_only_in_dir1
diff example/dir1/common_dir/dir1/common_dir
example/dir2/common_dir/dir1/common_dir
diff example/dir1/common_dir/dir2 example/dir2/common_dir/dir2
identical files : ['common_file', 'file_only_in_dir2',
'not_the_same']
common subdirectories : ['common_dir', 'dir_only_in_dir2',
'file_in_dir1']
diff example/dir1/common_dir/dir2/common_dir
example/dir2/common_dir/dir2/common_dir
diff example/dir1/common_dir/dir2/file_in_dir1
example/dir2/common_dir/dir2/file_in_dir1
diff example/dir1/common_dir/dir2/dir_only_in_dir2
example/dir2/common_dir/dir2/dir_only_in_dir2

程序内使用差异性

除了生成报告之外,dircmp() 还可以在程序中比较两个目录并且使用比较结果。下列的每个属性只有在使用时会被计算,所以创建一个 dircmp 的实例不会增加未使用数据的开销。

filecmp_dircmp_list.py

import filecmp
import pprint
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('left:')
pprint.pprint(dc.left_list)
print('\nright:')
pprint.pprint(dc.right_list)

目录中包含的被比较的文件和子目录可以使用 left_listright_list 列出。

$ python3 filecmp_dircmp_list.py
left:
['common_dir',
 'common_file',
 'dir_only_in_dir1',
 'file_in_dir1',
 'file_only_in_dir1',
 'not_the_same']
right:
['common_dir',
 'common_file',
 'dir_only_in_dir2',
 'file_in_dir1',
 'file_only_in_dir2',
 'not_the_same']

可以传入一个要忽略的文件名列表给构造器,默认是 rcscvstags

filecmp_dircmp_list_filter.py

import filecmp
import pprint
dc = filecmp.dircmp('example/dir1', 'example/dir2',
                    ignore=['common_file'])
print('left:')
pprint.pprint(dc.left_list)
print('\nright:')
pprint.pprint(dc.right_list)

这个例子中,「common_file」不在要比较的文件列表中。

$ python3 filecmp_dircmp_list_filter.py
left:
['common_dir',
 'dir_only_in_dir1',
 'file_in_dir1',
 'file_only_in_dir1',
 'not_the_same']
right:
['common_dir',
 'dir_only_in_dir2',
 'file_in_dir1',
 'file_only_in_dir2',
 'not_the_same']

两个目录的公共文件保存在 common 属性中,每个目录的唯一文件可以由 left_onlyright_only 获取。

filecmp_dircmp_membership.py

import filecmp
import pprint
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('common:')
pprint.pprint(dc.common)
print('\nleft:')
pprint.pprint(dc.left_only)
print('\nright:')
pprint.pprint(dc.right_only)

left」目录是传入 dircmp() 方法的第一个参数,「right」目录是第二个参数。

$ python3 filecmp_dircmp_membership.py
common:
['file_in_dir1', 'common_file', 'common_dir', 'not_the_same']
left:
['dir_only_in_dir1', 'file_only_in_dir1']
right:
['file_only_in_dir2', 'dir_only_in_dir2']

公共成员可以进一步分解为公共文件,公共目录以及 「funny」项目(存在于两个目录但有不同类型或者 os.stat() 返回出错)。

filecmp_dircmp_common.py

import filecmp
import pprint
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('common:')
pprint.pprint(dc.common)
print('\ndirectories:')
pprint.pprint(dc.common_dirs)
print('\nfiles:')
pprint.pprint(dc.common_files)
print('\nfunny:')
pprint.pprint(dc.common_funny)

这个例子中,file_in_dir1在一个目录中是一个文件,而在另一个中是个子目录,所以它才出现在 funny 列表中。

$ python3 filecmp_dircmp_common.py
common:
['file_in_dir1', 'common_file', 'common_dir', 'not_the_same']
directories:
['common_dir']
files:
['common_file', 'not_the_same']
funny:
['file_in_dir1']

文件之间的差异同样被分解。

filecmp_dircmp_diff.py

import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('same      :', dc.same_files)
print('different :', dc.diff_files)
print('funny     :', dc.funny_files)

not_the_same 仅仅比较了 os.stat() 返回的元信息并没有检查内容,
所以它出现在了 same_files 列表中。

$ python3 filecmp_dircmp_diff.py
same      : ['common_file', 'not_the_same']
different : []
funny     : []

最后还保存了子目录以允许简单的递归比较。

filecmp_dircmp_subdirs.py

import filecmp
dc = filecmp.dircmp('example/dir1', 'example/dir2')
print('subdirectories:')
print(dc.subdirs)

subdirs 属性是将目录名称映射到一个新的 dircmp 对象。

$ python3 filecmp_dircmp_subdirs.py
subdirectories:
{'common_dir': }

推荐阅读

  •  -- 比较两个序列之间的差异性。

本文章首发在 金年会app官方网 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 cc 协议,如果我们的工作有侵犯到您的权益,请及时联系金年会app官方网。

原文地址:https://learnku.com/docs/pymotw/filecmp-...

译文地址:https://learnku.com/docs/pymotw/filecmp-...

上一篇 下一篇
讨论数量: 0



暂无话题~
网站地图