filecmp --- 文件及目录的比较│Python标准库

前情提示: 测试代码中,右尖括号(>)表示命令行中输入的命令; 单独一行并以井字符(#)开头的为输出内容; 库的导入仅在本文的第一个测试代码中展现,其他代码块均省略库的导入代码。

  • 系统类型: Windows 10
  • python 版本: Python 3.9.0

filecmp 模块可以用于文件与文件之间或目录与目录之间的比较。并且可以通过设置参数来选取多种不同用时和不同准确性的方案。

filecmp 模块在进行文件或目录对比时,最终仅能返回是否相等这一结果。某些场景需要更加详细的结果说明,可以使用 difflib 标准库。

快捷函数

filecmp.cmp(f1, f2, shallow=True)
参数:
    f1, f2: 要进行比较的两个文件
    shallow: 关键字参数, 参数值为布尔值, 默认为 True;
             如果为 True, 则判断两文件需要具有相同的 os.stat() 签名才会认为是相等的;
             如果为 False, 则比较两文件的内容;
返回值:
    布尔值, 两个文件是否相等

比较 f1f2 的文件,如果它们似乎相等则返回 True,否则返回 False。在官方文档中用上了 '似乎' 一词,这让人感觉有什么隐秘的信息文档上没有表达出来。

import filecmp
import os

'''本次测试代码中存在 4 个待对比文件, 其中 文件1 与 文件2 内容不同, 文件3 与 文件4 内容相同'''
print(os.stat('文件1'))
print(os.stat('文件2'))
print(filecmp.cmp('文件1', '文件2', shallow=True))
print(filecmp.cmp('文件1', '文件2', shallow=False))
# os.stat_result(st_mode=33206, st_ino=1407374883609775, st_dev=3098197482, st_nlink=1, st_uid=0, st_gid=0, st_size=4, st_atime=1611109066, st_mtime=1611109066, st_ctime=1611043715)
# os.stat_result(st_mode=33206, st_ino=1688849860320432, st_dev=3098197482, st_nlink=1, st_uid=0, st_gid=0, st_size=4, st_atime=1611045689, st_mtime=1611045689, st_ctime=1611043722)
# True
# True

当比较内容相同的 文件1文件2 时,参数 shallow 无论设置为 True 还是 False 结果都是 True。按照文档所述,文件1文件2os.stat() 是不相同的,当参数 shallow 设置为 True 时,根据两个文件的 os.stat() 最终应该得到 False。为什么实际运行和文档描述不同呢?

查阅一些资料后,找到了一个比较合理的解释,当参数 shallow 设置为 True,那么 os.stat() 相同的会直接被视为相等,当两个文件的 os.stat() 不同时,依旧会对比文件中的内容。

另外,此函数会缓存比较结果,在下次比较时直接返回缓存结果。如果文件的 os.stat() 变化了,也就是文件被修改了,缓存自动失效。缓存也能用下文中的 filecmp.clear_cache() 函数清除。

filecmp.cmpfiles(dir1, dir2, common, shallow=True)
参数:
    dir1, dir2: 目录
    common: 需要对比的文件名列表
    shallow: 关键字参数, 参数值为布尔值, 默认为 True;
             如果为 True, 则判断两文件需要具有相同的 os.stat() 签名才会认为是相等的;
             如果为 False, 则比较两文件的内容;
返回值:
    元组, 包含三个类型为列表的元素.

比较两个目录下的指定文件,返回对比结果,返回值是包含三个类型为列表的元素的元组。

'''
文件目录如下, 其中, 两个目录内的 文件a 内容相同, 文件c 内容不同
- 目录1
    - 文件a
    - 目录b
        - 文件c
    - 文件d
- 目录2
    - 文件a
    - 目录b
        - 文件c
'''

'''对比两目录下的文件'''
print(filecmp.cmpfiles('目录1', '目录2', ['文件a', '目录b/文件c', '文件d']))
# (['文件a'], ['目录b/文件c'], ['文件d'])

参数 common 列举要对比的文件名,分别对比两个目录下的同名文件,如果两个文件相同,则加入返回值的第一个元素内; 如果两个文件内容不同,则加入返回值的第二个元素内; 如果文件无权限读取或者文件在任一目录内缺失,则加入返回值的第三个元素内;

参数 shallow 与上文中 filecmp.cmp() 函数意义相同。

filecmp.clear_cache()

清除 filecmp 缓存。一般情况下,文件修改后,文件的 os.stat() 自然会改变,缓存也会自动失效。但是如果文件被过快的修改,以至于超过底层文件系统记录修改时间的精度时,那么此后的文件对比将可能会出现问题,此函数就是为了解决这个问题。

但是,这种文件过快的修改不知道怎样测试出来? 大家有知道的吗?

dircmp 类

class filecmp.dircmp(a, b, ignore=None, hide=None)
参数:
    a, b: 目录
    ignore: 关键字参数, 需要忽略的文件名列表, 默认为 filecmp.DEFAULT_IGNORES
    hide: 关键字参数, 需要隐藏的文件名列表, 默认为 [os.curdir, os.pardir]

创建一个用于比较两个目录的目录比较对象。参数 ignore 可忽略指定的文件名,hide 可隐藏指定的文件名。

print(filecmp.DEFAULT_IGNORES)
# ['RCS', 'CVS', 'tags', '.git', '.hg', '.bzr', '_darcs', '__pycache__']
print(os.curdir)
# .
print(os.pardir)
# ..

dircmp 类中有许多属性,这里使用测试代码直接展示:

dircmp_test = filecmp.dircmp('目录1', '目录2')

'''第一个(相对左边)参数, 也是第一个目录的名称'''
print(dircmp_test.left)
# 目录1

'''第二个(相对右边)参数, 也是第二个目录的名称'''
print(dircmp_test.right)
# 目录2

'''经参数 hide 与参数 ignore 过滤后, 第一个目录内的所有文件与子目录'''
print(dircmp_test.left_list)
# ['文件a', '文件d', '目录b']

'''经参数 hide 与参数 ignore 过滤后, 第二个目录内的所有文件与子目录'''
print(dircmp_test.right_list)
# ['文件a', '目录b']

'''同时存在两个目录下的文件与子目录'''
print(dircmp_test.common)
# ['文件a', '目录b']

'''仅存在第一个目录下的文件与子目录'''
print(dircmp_test.left_only)
# ['文件d']

'''仅存在第二个目录下的文件与子目录'''
print(dircmp_test.right_only)
# []

'''同时存在两个目录下的子目录'''
print(dircmp_test.common_dirs)
# ['目录b']

'''将 common_dirs 属性值映射为 dircmp 对象的字典'''
print(dircmp_test.subdirs)
# {'目录b': }

'''同时存在两个目录下的文件'''
print(dircmp_test.common_files)
# ['文件a']

'''在两个目录中类型不同的名字,或者那些 os.stat() 报告错误的名字'''
print(dircmp_test.common_funny)
# []

'''在两个目录下, 使用类的比较操作符相同的文件'''
print(dircmp_test.same_files)
# ['文件a']

'''在两个目录下, 使用类的比较操作符不同的文件'''
print(dircmp_test.diff_files)
# []

'''无法比较的文件'''
print(dircmp_test.funny_files)
# []

dircmp 类也提供了一些用于打印信息的方法,同样,直接在测试用例中展示:

'''将 a 与 b 之间的比较打印'''
dircmp_test.report()
# diff 目录1 目录2
# Only in 目录1 : ['文件d']
# Identical files : ['文件a']
# Common subdirectories : ['目录b']

'''打印 a 与 b 及共同直接子目录的比较结果'''
dircmp_test.report_partial_closure()
# diff 目录1 目录2
# Only in 目录1 : ['文件d']
# Identical files : ['文件a']
# Common subdirectories : ['目录b']
#
# diff 目录1\目录b 目录2\目录b
# Differing files : ['文件c']

'''打印 a 与 b 及共同子目录比较结果(递归地)'''
dircmp_test.report_full_closure()
# diff 目录1 目录2
# Only in 目录1 : ['文件d']
# Identical files : ['文件a']
# Common subdirectories : ['目录b']
#
# diff 目录1\目录b 目录2\目录b
# Differing files : ['文件c']

公众号 : 「python杂货铺」,专注于 python 语言及其相关知识。发掘更多原创文章,期待您的关注。
image

参考资料

官方文档

源代码

python – filecmp.cmp()忽略不同的os.stat()签名?

你可能感兴趣的:(python程序员后端)