# 开发文件内容查重工具:从文本预处理到相似度算法实践


在文档管理、代码审查或内容创作中,重复或高度相似的文件会带来诸多问题——占用存储空间、增加检索复杂度,甚至引发版权纠纷。手动识别这些文件既低效又容易遗漏,因此我们需要一个自动化工具来解决这一痛点。本文将带你实现一个文件内容查重工具,支持文本预处理、多种相似度算法,并输出高相似文件对,帮助你高效管理文档。

实现思路分析

我们的工具将围绕文件遍历→文本预处理→相似度计算→结果输出四个核心环节展开:

  1. 文件遍历:使用glob模块递归扫描目标文件夹,收集所有文本文件(.txt、.md、.csv等)。
  2. 文本预处理:通过正则表达式去除标点、合并空格、统一小写,消除格式差异对相似度的影响。
  3. 相似度计算:提供两种算法选择:
    • 编辑距离(简化版):适合短文本或精确匹配场景,通过计算字符串修改差异度(插入、删除、替换次数)衡量相似度。
    • 余弦相似度(进阶版):适合长文本或语义相似场景,将文本转为TF-IDF向量,通过向量夹角的余弦值衡量语义重叠度。
  4. 结果输出:筛选相似度≥80%的文件对,格式化输出文件名和相似度,支持区分“完全重复”和“高度重叠”。

代码实现(Python)

下面是完整的代码实现,包含两种相似度算法,可根据需求选择:

import os
import re
import glob
import Levenshtein  # 需安装:pip install python-Levenshtein
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

def collect_text_files(folder_path):
    """遍历文件夹,收集所有文本文件(.txt、.md、.csv)"""
    file_extensions = ['*.txt', '*.md', '*.csv']
    all_files = []
    for ext in file_extensions:
        # 递归匹配所有子文件夹中的目标文件
        files = glob.glob(f"{folder_path}/**/{ext}", recursive=True)
        all_files.extend(files)
    return all_files

def preprocess_text(text):
    """文本预处理:去除标点、空格,转小写"""
    # 去除标点(保留字母、数字、下划线、空格)
    text = re.sub(r'[^\w\s]', '', text)
    # 转小写、去除首尾空格、合并连续空格
    text = text.lower().strip()
    text = re.sub(r'\s+', ' ', text)
    return text

def edit_distance_similarity(text1, text2):
    """基于编辑距离计算相似度:1 - 距离/最大长度"""
    if not text1 and not text2:
        return 1.0  # 两个空文本视为完全相同
    max_len = max(len(text1), len(text2))
    if max_len == 0:
        return 1.0  # 避免除以0
    # 计算编辑距离(Levenshtein距离)
    distance = Levenshtein.distance(text1, text2)
    # 转换为相似度:距离越小,相似度越高
    similarity = 1 - (distance / max_len)
    return similarity

def cosine_similarity_method(texts):
    """基于TF-IDF向量和余弦相似度计算文本对的相似度矩阵"""
    vectorizer = TfidfVectorizer()
    # 向量化所有文本(即使有空字符串,向量器也能处理)
    tfidf_matrix = vectorizer.fit_transform(texts)
    # 计算所有文本对的余弦相似度矩阵(n×n,n为文本数)
    similarity_matrix = cosine_similarity(tfidf_matrix, tfidf_matrix)
    return similarity_matrix

def main(folder_path, threshold=0.8, method='edit_distance'):
    """主函数:整合文件遍历、预处理、相似度计算、结果输出"""
    # 1. 收集目标文件夹中的所有文本文件
    files = collect_text_files(folder_path)
    if not files:
        print(f"错误:在路径 {folder_path} 中未找到文本文件!")
        return

    # 2. 读取并预处理文件内容
    file_contents = []
    for file in files:
        try:
            with open(file, 'r', encoding='utf-8') as f:
                content = f.read()
                processed = preprocess_text(content)
                file_contents.append(processed)
        except Exception as e:
            print(f"读取文件 {file} 失败:{e}")
            file_contents.append("")  # 标记为无效内容,避免后续计算出错

    # 3. 计算文件对的相似度
    similar_pairs = []
    if method == 'edit_distance':
        # 遍历所有文件对(i<j,避免重复计算)
        for i in range(len(files)):
            for j in range(i + 1, len(files)):
                text1 = file_contents[i]
                text2 = file_contents[j]
                sim = edit_distance_similarity(text1, text2)
                if sim >= threshold:
                    similar_pairs.append((files[i], files[j], sim))
    else:  # 余弦相似度
        similarity_matrix = cosine_similarity_method(file_contents)
        for i in range(len(files)):
            for j in range(i + 1, len(files)):
                sim = similarity_matrix[i, j]
                if sim >= threshold:
                    similar_pairs.append((files[i], files[j], sim))

    # 4. 输出相似文件对(仅显示文件名,隐藏路径)
    print("===== 相似文件分析结果 =====")
    for pair in similar_pairs:
        file1, file2, sim = pair
        sim_percent = sim * 100
        label = "(内容完全重复)" if sim == 1.0 else "(内容高度重叠)"
        print(f"- {os.path.basename(file1)} 与 {os.path.basename(file2)}:相似度 {sim_percent:.1f}% {label}")

if __name__ == "__main__":
    # 示例:请在当前目录下创建 test_files 文件夹,放入示例文件
    test_folder = "test_files"
    # 选择算法:'edit_distance'(编辑距离)或 'cosine'(余弦相似度)
    main(test_folder, threshold=0.8, method='edit_distance')

代码解释与运行指南

  1. 文件遍历collect_text_filesglob 递归扫描文件夹,支持通配符匹配多类文本文件。
  2. 文本预处理preprocess_text 通过正则表达式去除标点、统一大小写、合并空格,消除格式干扰。
  3. 相似度算法
    • 编辑距离:通过 Levenshtein.distance 计算字符串差异,转换为相似度(值越接近1,相似度越高)。适合短文本、精确匹配场景。
    • 余弦相似度:用 TfidfVectorizer 将文本转为TF-IDF向量,通过向量夹角的余弦值衡量语义相似度。适合长文本、语义级相似性分析。
  4. 主函数逻辑:读取文件→预处理→计算相似度→筛选高相似对→格式化输出。

测试与扩展

测试步骤:

  1. 创建 test_files 文件夹,放入示例文件:
    • file1.txtHello world, this is a test.
    • file2.txtHello world, this is a test.
    • file3.txtHello world, this is another test.
  2. 运行代码,选择 method='edit_distance''cosine',观察输出是否与问题描述一致。

扩展方向:

  • GUI界面:结合 tkinterPyQt 开发图形界面,支持文件夹选择、阈值设置。
  • 性能优化:对大文件夹,可通过“按长度分组”减少不必要的相似度计算(长度差异大的文件相似度大概率低)。
  • 算法升级:使用BERT等预训练模型生成文本向量,实现语义级相似度分析(需额外安装transformers库)。

总结

通过这个项目,我们实践了文件IO正则文本预处理两种经典相似度算法的应用。编辑距离适合“精确重复”检测,余弦相似度适合“语义相似”分析。工具虽简单,但覆盖了文本处理的核心流程,是入门文件分析与相似度算法的绝佳实践。未来可结合更先进的NLP技术(如大模型向量),进一步提升相似性识别的准确性与鲁棒性。