【Python】 文件目录比较工具filecmp和difflib
在一些运维场景中,常常需要比较两个环境中的应用目录结构(是否有文件/目录层面上的增删)以及比较两个环境中同名文件内容的不同(即文件层面上的改)。Python自带了两个内建模块可以很好地完成这个工作,filecmp和difflib。前者主要用于比较目录结构上的不同以及笼统的文件内容比较;后者用于比较两个文件具体内容上的不同。综合使用两个模块可以比较完备地做一次比较。
【filecmp】
filecmp提供一些方法可以很方便地进行对比两个目录在结构上的不同以及笼统的文件内容上的异同。比如
filecmp.cmp(f1,f2[,shallow]) 用于笼统地比较两个文件内容是否相同,shallow可以指定True或者False,当为True的时候这个方法会把文件的属性(os.stat方法调用看到的信息)也作为比较依据的一部分。整个方法最后返回True或者False告诉调用者两个文件的比较结果。
filecmp.cmpfiles(d1,d2,common[,shallow]) 用于比较两个目录下同名的那些文件是否都相同,common接受一个list或者tuple来表示比较哪些同名的文件。
除了上面了两个模块的静态方法之外,filecmp中还有一个dircmp类用于更加完备的比较处理工作。
dircmp类的定义是这么描述的:
dircmp(a,b,ignore=None,hide=None)A and B are directories.IGNORE is a list of names to ignore,defaults to ['RCS', 'CVS', 'tags'].HIDE is a list of names to hide,defaults to [os.curdir, os.pardir].
在构造完一个dircmp类对象之后,可以调用下面这几个方法来输出对比的信息
d.report() 只比较当前目录的内容,不涉及当前目录下子目录中内容是否相同。输出的结果是类似于下面这样的样子:
diff testdir1 testdir2Only in testdir1 : ['subdir1']Only in testdir2 : ['subdir2']Identical files : ['same.txt']Differing files : ['file.txt']
上面的结果中涉及到了只在目录A中、只在目录B中以及同名内容一致(identical)和同名内容不同(differing)四种类别的结果。其实除此之外还有可能得到同名子目录、因为某些问题而无法直接对比内容的文件(比如文件的内容无法hash)等结果。
d.report_partial_closure() 比较当前目录以及往下一级子目录的内容
d.report_full_closure() 递归比较当前目录下所有子目录,里面的内容。返回的格式化输出中,会按照各个子目录的不同分别列出对比情况。
上面的三个方法都是filecmp模块帮助我们格式化好的输出。如果需要获取第一手的比较结果,则应该调用dircmp类的一些其他属性。比如:
left_list 目录A中的子目录和文件列表,相当于os.listdir
right_list 目录B中的子目录和文件列表
left/right_only 仅存在于目录A/B中的子目录和文件
common 两目录中同名的子目录和文件
common_files 两目录中同名的子文件,内容不一定相同
common_dirs 两目录中同名的子目录,内容不一定相同
common_funny 两目录中同名的不可比较文件
same_files 两目录中同名且内容相同的子文件
diff_files 两目录中同名但内容不相同的子文件
funny_files 两目录中同名但无法比较的子文件
以上所有属性都是返回了一个List,其中是各个文件/目录的名字
需要注意的是dircmp类默认只对比当前目录层级,对于想要深入递归地对比后辈目录的话就需要采取一些手段。可以使用dircmp的subdirs这个属性
subdirs 是一个字典,字典的键是当前dircmp类对象对比的两个目录下同名的子目录(也就是说subdirs.keys()等于是common_dirs),每个键对应的值是另一dircmp对象,而这对象对比的就是两个同名子目录的下的内容了。也就是说不断地调取subdirs这个属性就可以实现递归比较了。虽然在这次应用中我没有采取用subdirs而是稍微麻烦一点采用了判断common_dirs的办法,但是两者原理是一样的。我的尝试待会儿写在下面
【difflib】
difflib深入到文件内部,不仅仅给出“文件内容不相同”级别的提示,而是具体说明了哪些地方不相同。常用于文本文件的比较。可以看出,使用difflib还是需要被对比的文件是可以被hash的。下面的说明将基于文本文件的对比来。
既然涉及到了详细的文件内容,那么就需要有一种表示给人看的,呈现文件内容对比结果的方式。字符界面上比较常见的方法,是像linux中的diff命令的结果那样。比如:
[root@localhost tmp]# cat file1 this is file1 my name is takanashi today is a little bit cold tomorrow is holiday [root@localhost tmp]# cat file2 this is file2 my name is Takanashi today is a little bit cold i wouldnt work tomorrow [root@localhost tmp]# diff file1 file2 1,2c1,2 < this is file1 < my name is takanashi --- > this is file2 > my name is Takanashi 4c4 < tomorrow is holiday --- > i wouldnt work tomorrow
关于字符提示的详细说明这里就不多提了,可以参看linux篇的介绍。这里提到主要是想说明,difflib这个模块的一些方法也是会输出这样形式的对比结果的。
difflib模块给出了一些用于比较文本的类,最简单的一种是Differ
■ Differ类
Differ类有compare方法用于直接比较两段文本,比较的结果是通过类似上面那种表现形式来呈现。比如上面的file1和file2两个文件,通过这样一个脚本来比较:
import difflibd = difflib.Differ() with open('file1','r') as file1:content1 = file1.read().splitlines() with open('file2','r') as file2:content2 = file2.read().splitlines()print '\n'.join(d.compare(content1,content2))
可以注意到,Differ类处理的对象并不是一块文本(或者说字符串)而是一个列表,列表是根据文本字符串通过\n split出来的,这一点也适用于difflib其他一些工具类。所以说difflib其实是基于行的比较。
compare方法返回的是一个生成器,里面是各行比较的结果。运行上面代码输出是:
- this is file1? ^ + this is file2? ^ - my name is takanashi? ^ + my name is Takanashi? ^ today is a little bit cold- tomorrow is holiday+ i wouldnt work tomorrow
两文件的前两句有所不同,比较结果前有'-'号的表示left_only,'+'号表示right_only,此外对于类似的行difflib会做进一步比较找出变更的地方。在相关行下方额外添加一行?开头的行,这行中的^号标识出变更发生的位置。两文件第三行是相同的,所以只输出了一遍,行前空格是为了和上下行对齐。后面两行因为差别较大,被认为是各自独特的行所以没有?开头的那行了。
另外,如果是B文件中某一行比A文件中的行增加或减少了一些字符,那么在?开头那行里会用-和+来表示增减字符是哪些。比如:
- this a file12 ? -+ this is a file2 ? +++
■ unified_diff方法
上面说Differ类会把相同的行打印一次。如果两文件相同部分很多,只有一点不同,那么把相同的行都显示出来,即使只打印一遍也还是有点不好的。而difflib.unified_diff方法可以解决这一个问题。
unified_diff方法接受n这个参数表名只显示发现不同处上下各n行的内容。相似的方法还有context_diff。不太用所以不详细展开了
■ SequenceMatcher类
这个类首先可以用于指定忽略一些字符的比较。在其构造方法中指定第一个参数是函数对象。这个函数接受一个字符并且经过一定判断后返回True或False。根据这个返回结果类将判断要不要把这个字符计入比较结果。比如s = SequenceMatcher(lambda x : x == ' ','some string A',' some string B')。
上面这个s可以调用方法s.find_longest_match(ab,ae,bb,be)。这个方法返回的是元组(i,j,k),表示上面比较的字符串A,B中A[ab:ae]与B[bb:be]两部分中可以找到最长公共部分A[i:i+k]和B[j:j+k]。
这个类另一个NB的地方在于不仅仅可以对比字符串,而可以对比任何序列。比如两个列表的对比,也可以通过它来实现。此时构造方法的第一个参数那个函数对象,接受的就不是一个字符而是一个序列中的元素了。
■ HtmlDiff类
这个类是我用的,它在Differ类的基础上将原先字符界面的结果呈现改成了更加友好的html界面显示。一目了然
构造方法:__init__(tabsize=8, wrapcolumn=None, linejunk=None, charjunk=IS_CHARACTER_JUNK)
tabsize是在html中显示的制表符的空格数量,默认是8但是我觉得太大了,改成2或者4更好看一些。wrapcolumn指定界面上的比较栏中文字最大宽度,超过此宽度会自动换行。默认是None也就是不换行,在碰到有很长的行的时候,页面宽度就会很大。linejunk和charjunk就是和ndiff方法中的相关参数差不多的,两个都是函数对象,用于指出怎么样的行或者怎么样的字符不计入比较。
HtmlDiff类主要用两个方法,make_table和make_file,两者参数类似,只不过前者返回的是可以组成一个独立html文件的html代码(带<html><head>等标签),而后者是生成一个html表格的代码(从<table>开始)。以make_file为例,其参数是make_file(fromlines, tolines [, fromdesc][, todesc][, context][, numlines])。fromlines和tolines是承载比较内容的两个列表,如上面所说,不是字符串是字符串经过'\n'split过的列表。然后fromdesc和todesc用于指定生成的html中对比栏目中抬头的文字。context参数默认是False,如果设置为True,那么html中只会显示有变化的行上线numlines行数的内容,大部分相同的内容就不重复显示了。
讲了半天,下面是我对HtmlDiff类的一个使用:
def check_diff(self, index, wrapcolumn):file1, file2 = self.differing[index]with open(file1, 'r') as f:content1 = f.read().splitlines()with open(file2, 'r') as f:content2 = f.read().splitlines()htmlDiff = HtmlDiff(tabsize=2,wrapcolumn=wrapcolumn)with open('tmp.html', 'w') as f:f.write(htmlDiff.make_file(content1, content2, fromdesc=self.dir1, todesc=self.dir2))webbrowser.open('tmp.html')
这是部分代码,结合了webbrowser模块之后,可以把生成的HTML对比文件立刻打开,得到的HTML界面大概长这样:
可以看到是比较友好的界面。
【Python】 文件目录比较工具filecmp和difflib相关推荐
- python模块管理工具,Python的包管理工具
Python的包管理工具 python包管理工具 python包管理工具简介 distribute是setuptools的取代,pip是easy_install的取代. Distribute是对标准库 ...
- 可爱的python测试开发库(python测试开发工具库汇总)
2019独角兽企业重金招聘Python工程师标准>>> 欢迎转载,转载请注明来源: github地址 谢谢点赞 本文地址 Python测试开发库 参考资料 https://githu ...
- (Python编程)目录工具
Programming Python, 3rd Edition 翻译 最新版本见wiki:http://wiki.woodpecker.org.cn/moin/PP3eD 欢迎参与翻译与修订. 4.3 ...
- Python 爬虫的工具列表( 附Github代码下载链接)
Python 爬虫的工具列表( 附Github代码下载链接) 这个列表包含与网页抓取和数据处理的Python库 网络 通用 urllib -网络库(stdlib). requests -网络库. gr ...
- Python包管理工具Distribute的安装
Python包管理工具Distribute的安装 Python的包管理工具常见的有easy_install, setuptools, 还有pip, distribute,那麽这几个工具有什么关系呢,看 ...
- 这些Python常用的工具和学习资源你都知道么?
经常收到读者的疑问,公众号的代码排版真的很好看,究竟用的什么开发工具呢?在这里,统一回复一下大家,公众号的排版用的是 MarkDown Here,而我平时使用的Python开发工具是Pycharm.这 ...
- python学习一(python与pip工具下载与安装)
python学习一(python与pip工具下载与安装) 一 Python下载 二 安装Python 三 安装 pip 3.1 采用cd命令进入到Scripts 目录下面 3.2 输入命 ...
- python机器学习可视化工具Yellowbrick介绍及平行坐标图实战示例
python机器学习可视化工具Yellowbrick介绍及平行坐标图实战示例 目录 python机器学习可视化工具Yellowbrick介绍及平行坐标图实战示例 yellowbrick简介及安装
- python机器学习可视化工具Yellowbrick绘图获取最佳聚类K值实战示例
python机器学习可视化工具Yellowbrick绘图获取最佳聚类K值实战示例 目录 机器学习可视化工具Yellowbrick绘图获取最佳聚类K值实战示例
最新文章
- VBA中级班课时3小结
- 如何使用意图将对象从一个Android活动发送到另一个?
- [Ubuntu]更改所有子文件和子目录所有者权限
- 人月神话之阅读笔记01
- 软件测试设计与开发,软件测试技术·测试设计与开发.ppt
- hive安装mysql驱动_Hadoop-2.6.0为基础的Hive安装
- Linux搜寻文件或目录命令解析
- 暂且解决INSTALL_FAILED_SHARED_USER_INCOMPATIBLE错误
- linux 程序 监听端口,linux和windows下如何知道端口是被那个程序监听占用?
- 红旗Linux 网卡bond,Linux双网卡绑定一个IP的实现
- Veritas Backup Exec 21设置备份任务
- MicroSip客户端编译、运行
- 高等数学学习笔记——第九十二讲——函数项级数收敛与一致收敛
- 设置二级域名解析到同IP不同端口
- ExtJs4 笔记(2) ExtJs对js基本语法扩展支持
- 2008~2009流行歌曲
- 真菌元胞自动机Python实现
- win11小鹤双拼改回全拼
- leelen可视对讲怎么接线_楼道对讲门铃怎么接线
- install firebox on ubuntu
热门文章
- 通过.obj生成2d图像_自动生成 凹凸法线灯贴图 插件
- 在我方某前沿防守地域 matlab,蒙特卡洛方法模拟小例子
- android intent例程,Android开发(四)| 探究活动(详解Intent+大量实例)
- php 自定义属性,HTML5的data-*自定义属性是什么-
- xampp python linux,Ubuntu的XAMPP着运行python脚本
- java内存栅栏_内存屏障(Memory Barriers/Fences) - 并发编程中最基础的一项技术
- 蓝牙模块引起电路干扰
- 2021年春季学期-信号与系统-第五次作业参考答案-第十一移小题—MATLAB
- RT-Thread智能车培训计划-2021
- 电抗电路的串并联的转换