本文目录导读:

我来介绍几种编写文件差异对比脚本的方法,涵盖不同场景和复杂度。
简易版本(使用系统命令)
Linux/Unix 系统
#!/bin/bash
# diff_files.sh
if [ $# -ne 2 ]; then
echo "用法: $0 <文件1> <文件2>"
exit 1
fi
if [ ! -f "$1" ] || [ ! -f "$2" ]; then
echo "错误: 文件不存在"
exit 1
fi
echo "=== 差异对比结果 ==="
diff "$1" "$2"
Python 实现(功能增强版)
基础版本
#!/usr/bin/env python3
# file_diff.py
import difflib
import sys
def compare_files(file1, file2):
"""对比两个文件并输出差异"""
try:
with open(file1, 'r', encoding='utf-8') as f1:
lines1 = f1.readlines()
with open(file2, 'r', encoding='utf-8') as f2:
lines2 = f2.readlines()
# 创建差异比较器
differ = difflib.HtmlDiff()
# 生成HTML格式的差异报告
html_diff = differ.make_file(
lines1, lines2,
fromdesc=file1,
todesc=file2
)
# 生成文本格式的差异
text_diff = difflib.unified_diff(
lines1, lines2,
fromfile=file1,
tofile=file2,
lineterm='\n'
)
return list(text_diff), html_diff
except FileNotFoundError as e:
return None, str(e)
def print_text_diff(diff_lines):
"""打印文本差异"""
for line in diff_lines:
if line.startswith('+'):
print(f" \033[32m{line}\033[0m") # 绿色(新增)
elif line.startswith('-'):
print(f" \033[31m{line}\033[0m") # 红色(删除)
elif line.startswith('@@'):
print(f" \033[33m{line}\033[0m") # 黄色(位置)
else:
print(f" {line}")
def main():
if len(sys.argv) != 3:
print("用法: python file_diff.py <文件1> <文件2>")
sys.exit(1)
file1 = sys.argv[1]
file2 = sys.argv[2]
text_diff, html_diff = compare_files(file1, file2)
if text_diff:
print(f"差异对比结果 ({file1} vs {file2}):")
print("=" * 50)
# 生成HTML报告
with open('diff_report.html', 'w', encoding='utf-8') as f:
f.write(html_diff)
print_text_diff(text_diff)
print(f"\nHTML报告已保存到: diff_report.html")
else:
print(f"文件完全相同 或 错误: {html_diff}")
if __name__ == "__main__":
main()
增强版本(支持更多功能)
#!/usr/bin/env python3
# advanced_file_diff.py
import hashlib
import difflib
import os
import sys
from collections import Counter
class FileComparer:
def __init__(self, file1, file2, encoding='utf-8'):
self.file1 = file1
self.file2 = file2
self.encoding = encoding
def read_files(self):
"""读取文件内容"""
try:
with open(self.file1, 'r', encoding=self.encoding) as f:
content1 = f.read()
with open(self.file2, 'r', encoding=self.encoding) as f:
content2 = f.read()
return content1, content2
except Exception as e:
print(f"读取文件失败: {e}")
return None, None
def file_hash(self, filename):
"""计算文件哈希值"""
with open(filename, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
def quick_check(self):
"""快速检查是否相同"""
return self.file_hash(self.file1) == self.file_hash(self.file2)
def get_missing_lines(self):
"""找出每个文件独有的行"""
content1, content2 = self.read_files()
if content1 is None or content2 is None:
return {}, {}
lines1 = content1.splitlines()
lines2 = content2.splitlines()
# 使用Counter统计每行的出现次数
counter1 = Counter(lines1)
counter2 = Counter(lines2)
# 找出独有的行
missing_in_file2 = counter1 - counter2
missing_in_file1 = counter2 - counter1
return dict(missing_in_file2), dict(missing_in_file1)
def generate_side_by_side_diff(self):
"""生成并排对比"""
content1, content2 = self.read_files()
if content1 is None or content2 is None:
return None
lines1 = content1.splitlines()
lines2 = content2.splitlines()
# 使用SequenceMatcher进行智能对比
matcher = difflib.SequenceMatcher(None, lines1, lines2)
side_by_side = []
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
if tag == 'equal':
for k in range(i1, i2):
side_by_side.append(f" | {lines1[k]} | {lines2[j1 + (k - i1)]} |")
elif tag == 'delete':
for k in range(i1, i2):
side_by_side.append(f"- | {lines1[k]} | {'':<20} |")
elif tag == 'insert':
for k in range(j1, j2):
side_by_side.append(f"+ | {'':<20} | {lines2[k]} |")
elif tag == 'replace':
for k in range(max(i2 - i1, j2 - j1)):
ln1 = lines1[i1 + k] if i1 + k < i2 else ""
ln2 = lines2[j1 + k] if j1 + k < j2 else ""
side_by_side.append(f"* | {ln1} | {ln2} |")
return side_by_side
def generate_statistics(self):
"""生成统计信息"""
content1, content2 = self.read_files()
if content1 is None or content2 is None:
return {}
lines1 = content1.splitlines()
lines2 = content2.splitlines()
stats = {
"文件1行数": len(lines1),
"文件2行数": len(lines2),
"总字符数(文件1)": len(content1),
"总字符数(文件2)": len(content2)
}
# 计算差异行数
matcher = difflib.SequenceMatcher(None, lines1, lines2)
diffs = 0
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
if tag != 'equal':
diffs += max(i2 - i1, j2 - j1)
stats["差异行数"] = diffs
stats["相似度百分比"] = round(100 * (1 - diffs/max(len(lines1), len(lines2), 1)), 2)
return stats
def main():
if len(sys.argv) != 3:
print("用法: python advanced_file_diff.py <文件1> <文件2>")
sys.exit(1)
file1, file2 = sys.argv[1], sys.argv[2]
if not os.path.exists(file1) or not os.path.exists(file2):
print("错误: 文件不存在")
sys.exit(1)
comparer = FileComparer(file1, file2)
print(f"对比文件: {file1} vs {file2}")
print("=" * 60)
# 快速检查
if comparer.quick_check():
print("✅ 文件完全相同")
else:
print("⚠️ 文件存在差异")
# 显示统计信息
stats = comparer.generate_statistics()
print("\n📊 统计信息:")
for key, value in stats.items():
print(f" {key}: {value}")
# 显示详细差异
print("\n📝 详细对比:")
side_by_side = comparer.generate_side_by_side_diff()
if side_by_side:
for line in side_by_side:
print(line)
# 显示独有的行
missing1, missing2 = comparer.get_missing_lines()
if missing1:
print(f"\n🔍 仅在 {file1} 中存在的行:")
for line, count in missing1.items():
print(f" × {line} (出现{count}次)")
if missing2:
print(f"\n🔍 仅在 {file2} 中存在的行:")
for line, count in missing2.items():
print(f" × {line} (出现{count}次)")
if __name__ == "__main__":
main()
使用第三方库(更强大的功能)
安装依赖
pip install filecmp2
使用 filecmp2
#!/usr/bin/env python3
# filecmp_diff.py
import filecmp
import sys
def compare_simple(file1, file2):
"""简单文件对比"""
comparison = filecmp.cmp(file1, file2, shallow=False)
if comparison:
print("✅ 文件完全相同")
else:
print("❌ 文件不同")
# 获取具体差异
result = filecmp.cmpfiles(
'.', '.', [file1, file2],
shallow=False
)
print(f"相同文件: {result[0]}")
print(f"不同文件: {result[1]}")
print(f"错误文件: {result[2]}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("用法: python filecmp_diff.py <文件1> <文件2>")
sys.exit(1)
compare_simple(sys.argv[1], sys.argv[2])
带GUI的可视化工具
#!/usr/bin/env python3
# gui_file_diff.py
import tkinter as tk
from tkinter import filedialog, ttk
import difflib
class FileDiffGUI:
def __init__(self, root):
self.root = root
self.root.title("文件差异对比工具")
self.root.geometry("800x600")
self.setup_ui()
def setup_ui(self):
"""设置界面"""
# 文件选择区域
file_frame = ttk.Frame(self.root, padding="10")
file_frame.grid(row=0, column=0, sticky=(tk.W, tk.E))
ttk.Button(file_frame, text="选择文件1",
command=self.select_file1).grid(row=0, column=0, padx=5)
self.file1_label = ttk.Label(file_frame, text="未选择")
self.file1_label.grid(row=0, column=1, padx=5)
ttk.Button(file_frame, text="选择文件2",
command=self.select_file2).grid(row=1, column=0, padx=5)
self.file2_label = ttk.Label(file_frame, text="未选择")
self.file2_label.grid(row=1, column=1, padx=5)
ttk.Button(file_frame, text="开始对比",
command=self.compare_files).grid(row=2, column=0, pady=10)
# 对比结果区域
self.result_text = tk.Text(self.root, wrap=tk.NONE, height=20)
self.result_text.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 滚动条
scrollbar = ttk.Scrollbar(self.root, orient="vertical",
command=self.result_text.yview)
scrollbar.grid(row=1, column=1, sticky=(tk.N, tk.S))
self.result_text.configure(yscrollcommand=scrollbar.set)
# 配置Grid权重
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(1, weight=1)
def select_file1(self):
filename = filedialog.askopenfilename()
if filename:
self.file1_label.config(text=filename)
self.file1 = filename
def select_file2(self):
filename = filedialog.askopenfilename()
if filename:
self.file2_label.config(text=filename)
self.file2 = filename
def compare_files(self):
"""执行文件对比"""
if not hasattr(self, 'file1') or not hasattr(self, 'file2'):
self.result_text.insert(tk.END, "请先选择两个文件!")
return
self.result_text.delete(1.0, tk.END)
try:
with open(self.file1, 'r', encoding='utf-8') as f1:
lines1 = f1.readlines()
with open(self.file2, 'r', encoding='utf-8') as f2:
lines2 = f2.readlines()
differ = difflib.unified_diff(lines1, lines2, fromfile='文件1', tofile='文件2')
for line in differ:
self.result_text.insert(tk.END, line)
except Exception as e:
self.result_text.insert(tk.END, f"错误: {e}")
if __name__ == "__main__":
root = tk.Tk()
app = FileDiffGUI(root)
root.mainloop()
使用 Bash 脚本(支持中文内容)
#!/bin/bash
# smart_diff.sh
compare_with_color() {
local file1="$1"
local file2="$2"
echo "文件: $file1 vs $file2"
echo "================================"
# 检查文件是否相同
if cmp -s "$file1" "$file2"; then
echo -e "\033[32m✅ 文件内容完全相同\033[0m"
return 0
fi
# 显示行数差异
local lines1=$(wc -l < "$file1")
local lines2=$(wc -l < "$file2")
echo "行数: 文件1=$lines1, 文件2=$lines2"
# 显示具体差异(带行号对比)
diff --side-by-side --suppress-common-lines "$file1" "$file2" | \
while IFS= read -r line; do
if [[ "$line" == *"|"* ]]; then
echo -e "\033[33m$line\033[0m" # 黄色表示修改
elif [[ "$line" == *"<"* ]]; then
echo -e "\033[31m$line\033[0m" # 红色表示删除
elif [[ "$line" == *">"* ]]; then
echo -e "\033[32m$line\033[0m" # 绿色表示新增
else
echo "$line"
fi
done
echo -e "\n差异统计:"
diff_lines=$(diff "$file1" "$file2" | grep -c '^[<>]')
echo -e "\033[31m删除: $(diff "$file1" "$file2" | grep -c '^<') 行\033[0m"
echo -e "\033[32m新增: $(diff "$file1" "$file2" | grep -c '^>') 行\033[0m"
echo "总差异: $diff_lines 行"
}
# 主程序
if [ "$#" -ne 2 ]; then
echo "用法: $0 <文件1> <文件2>"
exit 1
fi
compare_with_color "$1" "$2"
使用方法
-
保存脚本:将上述任一脚本保存为
.py或.sh文件 -
添加执行权限(如果使用脚本):
chmod +x script_name -
运行:
# Python版本 python3 file_diff.py file1.txt file2.txt # Bash版本 ./smart_diff.sh file1.txt file2.txt # GUI版本 python3 gui_file_diff.py
功能特点
- ✅ 支持彩色输出,便于区分新增/删除/修改
- ✅ 可生成统计信息(行数、相似度等)
- ✅ 支持并排对比模式
- ✅ 可生成HTML格式报告
- ✅ 支持大文件处理
- ✅ 可选择图形界面版本
根据你的具体需求选择合适的版本即可,如果只需要简单的比较,使用系统命令 diff 就足够了;如果需要更详细的分析和可视化,建议使用Python版本。