如何编写文件差异对比脚本

wen 实用脚本 3

本文目录导读:

如何编写文件差异对比脚本

  1. 简易版本(使用系统命令)
  2. Python 实现(功能增强版)
  3. 使用第三方库(更强大的功能)
  4. 带GUI的可视化工具
  5. 使用 Bash 脚本(支持中文内容)
  6. 使用方法
  7. 功能特点

我来介绍几种编写文件差异对比脚本的方法,涵盖不同场景和复杂度。

简易版本(使用系统命令)

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"

使用方法

  1. 保存脚本:将上述任一脚本保存为 .py.sh 文件

  2. 添加执行权限(如果使用脚本):chmod +x script_name

  3. 运行

    # 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版本。

抱歉,评论功能暂时关闭!