文件内容差异对比方法
介绍如何通过difflib模块实现文件内容差异对比。difflib作为Python的标准库模块无需安装,作用是对比文本之间的差异,且支持输出可读性比较强的HTML文档,与Linux下的 diff命令相似。我们可以使用difflib 对比代码、配置文件的差别,在版本控制方面是非常有用。
2.1.1 两个字符串的差异对比
- 传递文件内容,然后进行简单的行切割
- 创建differ函数
- 在differ对象的基础上,借助于compare方法将两个内容进行对比
- 结果以join的方式展示
import difflib
# 准备第一个文件
text1 = """text1:
This module provides classes and functions for comparing
includes
add string
"""
# 准备第二个文件
text1_line = text1.splitlines() # 以行进行分割,以便进行对比
text2 = """text2:
This module provides classes and functions for comparing
includes add string
"""
text2_line = text2.splitlines()
# 创建differ对象
d = difflib.Differ() #创建Differ对象
# 使用compare方法比较内容
diff = d.compare(text2_line,text1_line)
print(diff)
#<generator object Differ.compare at 0x000002249D0A7C80>
# 拼接比较后的结果
print('\n'.join(list(diff)))
<generator object Differ.compare at 0x0000024ABEC97C10>
- text2:
? ^
+ text1:
? ^
This module provides classes and functions for comparing
- includes add string
+ includes
+ add string
2.1.2 生成美观的HTML格式文档
采用 HtmIDiff()类的 make_file() 方法就可以生成美观的HTML文档
HtmlDiff() 类的make_file()方法:生成美观的HTML文档
- 传递文件内容,然后进行简单的行切割
- 创建HtmlDiff函数
- 在HtmlDiff对象的基础上,借助于make_file()方法将两个内容进行对比
- 直接查看生成的文件
效果:
颜色
统计
import difflib
# 准备第一个文件
text1 = """text1:
This module provides classes and functions for comparing
includes
add string
"""
# 准备第二个文件
text1_line = text1.splitlines() # 以行进行分割,以便进行对比
text2 = """text2:
This module provides classes and functions for comparing
includes add string
"""
text2_line = text2.splitlines()
# 创建differ对象
d = difflib.HtmlDiff() #创建Differ对象
# 使用compare方法比较内容
diff = d.make_file(text2_line,text1_line)
# 将输出的对比效果放到一个文件中 --with open 方法
with open('diff.html','w') as f:
f.write(diff)
2.1.3 对比nginx 配置文件差异
- 准备两个nginx文件
- 分别读取两个配置文件
- 读取内容
- 文件内容的基本判断
- 内容的比较
- 结果的输出
import difflib
import sys
# 1. 准备文件
# 格式: python pythonauto_30_文件对比-配置实践.py nginx.conf1 nginx.conf2
# 2. 读取文件
# argv -- 命令行;argv[0]脚本名,agrv[1]是第一个参数,依次类推
try:
textfile1 = sys.argv[1]
textfile2 = sys.argv[2]
# 如果在尝试读取命令行参数时发生了异常(例如,没有提供足够的参数),
# 则会捕获这个异常,并执行except块中的代码。
except Exception as e:
# str(e)将异常对象e转换为字符串,以便打印。
print(f"Error: str({e})")
print("Usage: diff-file.py filename1 filename2")
sys.exit()
# 3.读取内容
def readfile(filename):
try:
with open(filename,'r') as f:
text = f.read().splitlines() #读取后以行进行分隔
return text
except Exception as error:
print("读取文件错误:{}".format(error))
sys.exit()
# 4. 文件内容为空的基本判断
if textfile1 == "" or textfile2 == "":
print("脚本的参数不允许为空,请检查")
print(f"Usage: diff-file.py filename1 filename2")
sys.exit()
# 5. 内容的比较
# 5.1读取文件
text1_line = readfile(textfile1)
text2_line = readfile(textfile2)
# 5.2 创建Htmldiff对象
diff_object = difflib.HtmlDiff()
# 5.3 对文件进行比较
diff_result = diff_object.make_file(text1_line,text2_line)
# 6.输出结果
with open('nginx_diff.html','w') as f:
f.write(diff_result)
代码封装
定义了一个名为 DiffContent 的类,该类提供了两个主要功能:
- 比较两个文件的内容差异,并将差异结果保存为 HTML 文件;
- 以及比较两个字符串(文本)的差异,并同样将差异结果保存为 HTML 文件。
import difflib
import os.path
import time
from typing import List
class DiffContent:
@classmethod
def _read_file(cls, file_path: str) -> List[str]:
"""
读取指定路径的文件内容,并将其作为字符串列表返回,每个列表项代表文件的一行
:param file_path: 文件绝对路径
:return: 列表,包含文件的所有行
"""
try:
with open(file_path, "rb") as f:
# 二进制方式读取文件内容,并转换为str类型
lines = f.read().decode('utf-8')
# 按行进行分割
text = lines.splitlines()
return text
except Exception as e:
print("ERROR: {}".format(str(e)))
return [] # 返回空列表以避免后续错误
@classmethod
def compare_diff_by_file(cls, file1: str, file2: str, save_path: str = '') -> None:
"""
对比文件内容差异,并输出html文件,文件名命名:compare_file1name_file2name.html
:param file_1:文件1
:param file_2:文件2
:param save_path:如果未指定,则在当前目录下保存文件
:return:
"""
# 获取文件内容
file1_content = cls._read_file(file1)
file2_content = cls._read_file(file2)
# 创建比较器
compare = difflib.HtmlDiff()
res = compare.make_file(file1_content, file2_content)
# 获取输出html文件的绝对路径
file1_name = os.path.basename(file_1).split('.')[0]
file2_name = os.path.basename(file_2).split('.')[0]
if not file_1_name or not file_2_name:
raise ValueError("File names must contain an extension to generate a valid output file name.")
if save_path:
out_file = '{}/compare_{}_{}.html'.format(save_path, file_1_name, file_2_name)
else:
out_file = 'compare_{}_{}.html'.format(file_1_name, file_2_name)
with open(out_file, 'w') as f:
f.writelines(res)
@classmethod
def compare_text(cls, src_text: str, target_text: str, save_path: str = '') -> None:
"""
比较给定的2个字符串,并输出html文件
:param src_text:
:param target_text:
:param save_path:如果未指定,则在当前目录下保存文件
:return:
"""
compare = difflib.HtmlDiff()
compare_result = compare.make_file(src_text, target_text)
if save_path:
out_file = f"{save_path}/compare{int(time.time())}.html" # 使用整数时间戳避免微秒差异
else:
out_file = 'compare{}.html'.format(str(time.time()).split('.')[0])
with open(out_file, 'w') as f:
f.writelines(compare_result)