文件内容差异对比-difflib

两个字符串的差异对比

import difflib

text1="""Hello World
hello world
Hello World
"""
text1_lines=text1.splitlines()
text2="""Hello World
Hello World

"""
text2_lines=text2.splitlines()
#创建Differ()对象
d=difflib.Differ()
#采用compare方法进行比较
diff=d.compare(text1_lines,text2_lines)
print('\n'.join(list(diff)))
符号含义
'-'包含在第一个序列中,但不包含在第二个序列中
'+'包含在第二个序列中,但不包含在第一个序列中
''两个序列行一直
'?'标志两个序列存在增量差异
'^'标志两个序列行存在差异的字符

生成美观的对比HTML格式文档

import difflib

text1="""Hello World
hello world
Hello World
"""
text1_lines=text1.splitlines()
text2="""Hello World
Hello World
"""
text2_lines=text2.splitlines()
#创建Differ()对象
d=difflib.HtmlDiff()
print(d.make_file(text1_lines,text2_lines))

文件与目录差异对比方法-filecmp

单文件对比

采用filecmp.cmp(f1,f2[,shallow])方法,比较文件相同返回True,不同返回False,shallow默认为True,表示只根据os.stat()方法返回的文件基本信息进行对比,忽略文件内容的对比。反之则两种都需要。

import filecmp
print(filecmp.cmp("./test.txt","./test.png"))

多文件对比

采用filecmp.cpmfiles(dir1,dir2,common[,shallow])方法,对比目录给定的文件清单。方法返回文件名的三个列表,分别为匹配(包含匹配的文件的列表)、不匹配(不包含匹配的文件的列表)、错误(目录不存在文件、不具备权限等原因不能比较的文件清单)。

import filecmp

print(filecmp.cmpfiles("/tmp1/","/tmp2/",['test1.txt','text2.txt']))

目录对比

通过dircmp(a,b[,ignore[,hide]])类创建一个目录比较对象,其中a和b是参加比较的目录名。ignore代表文件名忽略的列表,并默认为['RCS','CVS','tags']; hide代表隐藏的列表,默认为[os.curdir,os.pardir]。dircmp类可以获得目录比较的详细信息,如只有在a目录中包括的文件、a与b都存在的子目录、匹配的文件等,同时支持递归。

dircmp提供了三个输出报告的方法:

  • report():比较当前指定目录中的内容;
  • report_partial_closure():比较当前指定目录及第一级子目录中的内容;
  • report_full_closure():递归比较所有指定目录的内容。

为输出更加详细的比较结果,dircmp类还提供了以下属性:

  • left:左目录,如类定义中的a;
  • right:右目录,如类定义中的b:
  • left_list:左目录中的文件及目录列表;
  • right_list:右目录中的文件及目录列表;
  • common:两边目录共同存在的文件或目录:
  • left_only:只在左目录中的文件或目录;
  • right_only:只在右目录中的文件或目录
  • common_dirs:两边目录都存在的子目录;
  • common_files:两边目录都存在的子文件:
  • common_funny:两边目录都存在的子目录(不同目录类型或osstat(记录的错误);
  • same_files:匹配相同的文件:
  • diff_files:不匹配的文件:
  • funny_files:两边目录中都存在,但无法比较的文件;
  • subdirs:将commondirs目录名映射到新的dircmp对象,格式为字典类型。

对比dir1与dir2的目录差异

通过调用dircmp()方法实现目录差异对比,同时输出对比对象所有属性信息

import filecmp
a="/tmp/" #定义左目录
b="/tmp/" #定义右目录
dirobj=filecmp.dircmp(a,b,['test.py'])#目录比较,忽略test.py文件
# #输出对比结果数据报表,详细说明请参考filecmp类方法及属性信息 dirobj.report()
dirobj.report()
dirobj.report_partial_closure()
dirobj.report_full_closure()
print("left list:"+str(dirobj.left_list))
print("right list:"+ str(dirobj.right_list))
print("common:"+str(dirobj.common))
print("left_only:"+str(dirobj.left_only))
print("right_only:"+ str(dirobj.right_only))
print("common_dirs:"+ str(dirobj.common_dirs))
print("common_files:"+ str(dirobj.common_files))
print("common_funny:"+ str(dirobj.common_funny))
print("same_file:"+ str(dirobj.same_files))
print("diff_files:"+str(dirobj.diff_files))
print("funny_files:"+ str(dirobj.funny_files))

检验源与备份目录差异

检验源与备份目录差异,对更新项进行复制

import os,sys
import filecmp
import re
import shutil
holderlist=[]
def compareme(dir1,dir2):#递归获取更新项函数
    dircomp=filecmp.dircmp(dir1,dir2)
    only_in_one=dircomp.left_only #源目录新文件或目录
    diff_in_one=dircomp.diff_files #不匹配文件,源目录文件已发生变化
    dirpath=os.path.abspath(dir1) #定义源目录绝对路径
    #将更新文件名或目录追加到holderlist
    [holderlist.append(os.path.abspath(os.path.join(dir1,x))) for x in only_in_one]
    [holderlist.append(os.path.abspath(os.path.join(dir1,x))) for x in diff_in_one]
    if len(dircomp.common_dirs)>0:#判断是否存在相同子目录,以便递归
        for item in dircomp.common_dirs:#递归子目录
            compareme(os.path.abspath(os.path.join(dir1,item)),os.path.abspath(os.path.join(dir2,item)))
    return holderlist

def main():
    if len(sys.argv)>2: #要求输入源目录与备份目录
        dir1=sys.argv[1]
        dir2=sys.argv[2]
    else:
        print("Usage:",sys.argv[0],"datadir backupdir")
        sys.exit()
    source_files = compareme(dir1, dir2)  # 对比源目录与备份目录
    dir1 = os.path.abspath(dir1)
    if not dir2.endswith('/'): dir2 = dir2 + '/'  # 备份目录路径加“/”符
    dir2 = os.path.abspath(dir2)
    destination_files = []
    createdir_bool = False
    for item in source_files:  # 遍历返回的差异文件或目录清单
        destination_dir=re.sub(dir1, dir2, item)  # 将源目录差异路径清单对应替换成
        # 备份目录
        destination_files.append(destination_dir)
        if os.path.isdir(item):  # 如果差异路径为目录且不存在,则在备份目录中创建
            if not os.path.exists(destination_dir):
                os.makedirs(destination_dir)
                createdirbool = True  # 再次调用compareme函数标记
    if createdir_bool:  # 重新调用compareme函数,重新遍历新创建目录的内容
        destination_files = []
        source_files = []
        source_files = compareme(dir1, dir2)  # 调用compareme函数
        for item in source_files:  # 获取源目录差异路径清单,对应替换成备份目录
            destination_dir = re.sub(dir1, dir2, item)
            destination_files.append(destination_dir)

    print("update item:")
    print(source_files) # 输出更新项列表清单
    copy_pair = zip(source_files, destination_files)  # 将源目录与备份目录文件清单拆分成元组
    for item in copy_pair:
        if os.path.isfile(item[0]):  # 判断是否为文件,是则进行复制操作
            shutil.copyfile(item[0], item[1])

if __name__ == '__main__':
    main()

List of learning reference documents

  • 《Python自动化运维技术与最佳实践-刘天斯》