第一次个人编程作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477
github链接 https://github.com/1989324880/3223004511

一、PSP表格

PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10 15
· Estimate · 估计这个任务需要多少时间 10 15
Development 开发 420 500
· Analysis · 需求分析 (包括学习新技术) 70 90
· Design Spec · 生成设计文档 25 30
· Design Review · 设计复审 20 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 15 30
· Design · 具体设计 50 80
· Coding · 具体编码 160 160
· Code Review · 代码复审 30 30
· Test · 测试(自我测试,修改代码,提交修改) 50 60
Reporting 报告 190 188
· Test Repor · 测试报告 60 70
· Size Measurement · 计算工作量 10 8
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 120 110
· 合计 620 703

二、计算模块接口的设计与实现过程

1.代码组织架构

1.1模块结构

PlagiarismCheck/
├── main.py # 主程序入口
├── text_processor.py # 文本处理模块
├── similarity_calculator.py # 相似度计算模块
├── file_handler.py # 文件操作模块
└── test_main.py # 单元测试

1.2核心类与函数设计

1.2.1 TextProcessor 类 - --文本预处理

class TextProcessor:
def preprocess_text(text: str) -> List[str]
def remove_punctuation(text: str) -> str
def chinese_segmentation(text: str) -> List[str]
def english_tokenization(text: str) -> List[str]

1.2.2. SimilarityCalculator 类 - -相似度计算

class SimilarityCalculator:
def get_tfidf_vectors(text1: str, text2: str) -> Tuple[List[float], List[float]]
def cosine_similarity(vec1: List[float], vec2: List[float]) -> float
def calculate_similarity(original: str, copied: str) -> float

1.2.3. FileHandler 类 - -文件操作

class FileHandler:
def read_file(file_path: str) -> str
def write_result(result: float, output_path: str)
def validate_file_path(file_path: str) -> bool

1.3关键函数流程图

deepseek_mermaid_20250922_119ba5

三、核心算法关键与独到之处

1. 算法关键

本模块核心采用 “TF-IDF 特征提取 + 余弦相似度度量” 的经典文本相似度算法,除此之外还具有以下算法核心创新点:
1.中英文混合分词:独创的中英文混合处理技术:中文字符按字分割,英文按单词分割;实时语言检测机制:无需预先指定文本语言类型;智能处理数字和特殊字符,保留有效信息

2.TF 计算和IDF 平滑处理:采用 “词频 / 文本总词数” 的归一化方式,避免长文本词频偏高的偏差;并且使用公式 log((N+1)/(DF+1)) + 1(N 为文档总数,DF 为含该词的文档数),避免 DF=0 时 IDF 无穷大的问题,同时增强低频词的区分度:

/平滑处理避免除零错误/
idf = math.log((N + 1) / (df + 1)) + 1
/归一化处理确保数值稳定性/
tf_idf = (tf / total_words) * idf

3.余弦相似度:通过向量点积与模长比值,量化两个文本的 “方向一致性”,值越接近 1 表示相似度越高,0 表示完全无关。

2. 独到设计

1.自适应文本清洗:除英文标点外,额外包含中文标点(如 “,。!”),解决中文文本预处理不彻底的问题;
2.空文本容错:在compute_text_similarity和calculate_tfidf_vectors中均添加空文本判断,避免因输入文本为空导致的除零错误或向量计算异常;
3.结果范围约束:通过max(0.0, min(1.0, similarity_score))确保相似度结果始终在 [0,1] 区间,符合业务预期。

四、计算模块接口的性能改进

function_execution_times

通过性能分析图发现,preprocess_text 和 calculate_tfidf_vectors 是性能瓶颈,耗时最长。

性能优化

1.preprocess_text 函数优化

原始问题分析:原实现采用逐字符遍历 + 条件判断的方式进行分词,存在三个主要问题:

  • 对每个字符进行多次类型判断(中文字符 / 英文字符 / 特殊字符)
  • 频繁进行字符串拼接操作(current_word += char)
  • 空白字符处理逻辑冗余

优化方案:

def preprocess_text(text):
"""优化后的文本预处理函数"""
# 去除标点符号(保留原有逻辑)
punctuation_chars = string.punctuation + ',。!?;:“”‘’【】()《》'
text = text.translate(str.maketrans('', '', punctuation_chars))
text = text.lower()

# 关键优化:使用正则表达式一次性匹配所有有效词汇
# 匹配规则:中文字符([\u4e00-\u9fff])或连续的英文/数字([a-zA-Z0-9]+)
pattern = re.compile(r'[\u4e00-\u9fff]|[a-zA-Z0-9]+')
return pattern.findall(text)

优化原理:

  • 利用正则表达式引擎的 C 语言底层实现,替代 Python 原生循环
  • 将多轮字符判断合并为一次正则匹配,时间复杂度从 O (n) 降至 O (1) 级
  • 消除字符串拼接操作,减少内存分配开销

2.calculate_tfidf_vectors 函数优化

原始问题分析:

  • 单独遍历词汇表计算文档频率(DF),存在二次遍历
  • 每次计算 TF 时重复调用len(words_text1),产生冗余计算
  • 词汇表构建方式低效(set(words_text1 + words_text2))

优化方案:

def calculate_tfidf_vectors(text1, text2):
"""优化后的TF-IDF向量计算函数"""
words_text1 = preprocess_text(text1)
words_text2 = preprocess_text(text2)

# 优化1:预计算文本长度,避免重复调用len()
len_text1 = len(words_text1)
len_text2 = len(words_text2)

# 优化2:合并词汇表构建与文档频率计算
vocabulary = set()
document_frequency = Counter()

# 处理第一个文本
for word in words_text1:
    vocabulary.add(word)
    document_frequency[word] = 1  # 标记至少在文本1中出现

# 处理第二个文本
for word in words_text2:
    vocabulary.add(word)
    if word in document_frequency:
        document_frequency[word] = 2  # 已在文本1中出现,现在文本2也出现
    else:
        document_frequency[word] = 1  # 仅在文本2中出现

# 优化3:预计算IDF值并复用
total_documents = 2
idf_cache = {}
for word in vocabulary:
    df = document_frequency[word]
    idf_cache[word] = math.log((total_documents + 1) / (df + 1)) + 1

# 计算TF-IDF向量
tf1 = Counter(words_text1)
tf2 = Counter(words_text2)

vector_text1 = []
vector_text2 = []

for word in vocabulary:
    idf = idf_cache[word]
    # 优化4:合并条件判断,减少分支跳转
    vector_text1.append((tf1[word] / len_text1) * idf if len_text1 > 0 else 0)
    vector_text2.append((tf2[word] / len_text2) * idf if len_text2 > 0 else 0)

return vector_text1, vector_text2

优化原理:

  • 预计算文本长度(len_text1/len_text2),将 O (1) 操作从循环中移出
  • 合并词汇表构建与 DF 计算,减少一次完整遍历(从 2 次遍历变为 2 次线性扫描)
  • 增加 IDF 缓存(idf_cache),避免重复计算对数函数

五、计算模块部分单元测试展示

1. 文本预处理测试

def test_preprocess_text_chinese(self):
"""测试中文文本预处理 - 验证中文字符正确分割"""
text = "你好,世界!这是一段中文文本。"
result = preprocess_text(text)
expected = ['你', '好', '世', '界', '这', '是', '一', '段', '中', '文', '文', '本']
self.assertEqual(result, expected)

测试数据构造思路:

  • 使用包含中文标点的完整句子
  • 验证分词算法对中文的处理准确性

2. 混合语言处理测试

def test_preprocess_text_mixed(self):
"""测试中英文混合文本预处理 - 验证多语言混合处理能力"""
text = "Hello你好,World世界!Python编程"
result = preprocess_text(text)
expected = ['hello', '你', '好', 'world', '世', '界', 'python', '编', '程']
self.assertEqual(result, expected)

测试数据构造思路:

  • 构造中英文交替出现的复杂文本
  • 文本包含标点符号和大小写字母

3. 边界条件测试

def test_compute_text_similarity_empty(self):
"""测试空文本的相似度计算 - 验证边界条件处理"""
similarity = compute_text_similarity("", "测试文本")
self.assertEqual(similarity, 0.0)
similarity = compute_text_similarity("测试文本", "")
self.assertEqual(similarity, 0.0)
similarity = compute_text_similarity("", "")
self.assertEqual(similarity, 0.0)

测试数据构造思路:

  • 测试空字符串输入的各种组合
  • 确保空文本返回合理的默认值(0.0)

4. 数学计算准确性测试

def test_cosine_similarity_identical(self):
"""测试相同向量的余弦相似度 - 验证数学计算正确性"""
vec1 = [1, 2, 3]
vec2 = [1, 2, 3]
result = calculate_cosine_similarity(vec1, vec2)
self.assertAlmostEqual(result, 1.0, places=5)

测试数据构造思路:

  • 使用简单的数值向量便于验证
  • 相同向量应该得到相似度1.0
  • 测试数学计算的精确度

测试覆盖率报告分析

1.总体覆盖率统计

联想截图_20250922203141

2.详细覆盖率分析

main.py 文件覆盖率:86%

image
1. preprocess_text(文本预处理函数)
覆盖率:87%
未覆盖原因推测:该函数需处理 “中英文混合、含特殊字符、长文本” 等多种场景,可能部分边缘场景(如极长文本的特殊分割、罕见标点组合)的测试用例缺失,导致 3 行代码未被执行。
2. calculate_tfidf_vectors(TF-IDF 向量计算函数)
覆盖率:93%
未覆盖原因推测:函数涉及 “词汇表构建、TF 计算、IDF 平滑” 等多步骤,可能在 “文档频率(DF)为 0 的极端情况” 或 “单文本全重复词汇” 场景下,存在 2 行代码未被测试覆盖。
3. calculate_cosine_similarity(余弦相似度计算函数)
覆盖率:100%
分析:测试用例覆盖了 “相同向量(相似度 1.0)、正交向量(相似度 0.0)、含零向量” 等核心场景,所有 6 行代码均被执行,逻辑验证充分。
4. compute_text_similarity(文本相似度计算函数)
覆盖率:100%
分析:测试用例覆盖了 “完全相同、完全不同、部分相似、空文本” 等场景,5 行核心逻辑均被执行,验证了从 “文本输入→向量生成→相似度计算” 的完整链路。
5. main(主函数)
覆盖率:82%
未覆盖原因推测:主函数涉及 “命令行参数解析、多文件 IO 调度、结果打印” 等流程,可能在 “参数数量异常(如 5 个参数)” 或 “文件读取中途失败” 等边缘流程中,存在 2 行代码未被测试覆盖。

test_main.py 文件覆盖率:99%

test_main.py是单元测试文件,大部分测试用例的覆盖率为 100%,仅最后一行(no function)显示有 1 行未覆盖,推测是测试文件中某段辅助代码(如临时变量清理、导入逻辑)未被完全执行,整体对核心测试逻辑影响极小。
image

六、异常处理设计

本模块围绕 “文件 IO 操作”“文本数据有效性”“命令行参数” 三大核心场景,设计了 6 类针对性异常处理机制。所有异常均遵循 “精准捕获 - 清晰提示 - 安全退出” 原则,确保程序在异常输入下不崩溃、用户能快速定位问题,同时通过单元测试覆盖所有异常场景,验证处理逻辑的有效性。

异常分类与详细说明

1. 文件不存在异常(FileNotFoundError)

设计目标
当用户指定的原文 / 抄袭文 / 输出文件路径错误(如文件被删除、路径拼写错误)时,主动捕获异常并提示 “文件不存在”,避免程序因 “找不到文件” 抛出未处理的系统异常,同时以退出码1正常退出,符合命令行工具的错误码规范。
触发场景
用户输入命令python main.py original.txt copied.txt output.txt,但copied.txt已被手动删除;
用户误写文件路径,如将data/original.txt写成data/orignial.txt(拼写错误)。
单元测试样例

def test_read_file_content_not_exists(self):
   with self.assertRaises(SystemExit):
   read_file_content("nonexistent_file.txt")

异常处理核心代码(read_file_content函数)

def read_file_content(file_path):
try:
    with open(file_path, 'r', encoding='utf-8') as file:
        return file.read()
except FileNotFoundError:
    print(f"错误:文件 {file_path} 不存在")  # 清晰提示错误原因
    sys.exit(1)  # 退出码1表示“文件相关错误”

2. 文件读取通用异常(Exception)

设计目标
覆盖除 “文件不存在” 外的所有文件读取错误(如权限不足、文件损坏、编码错误),捕获未知异常并打印具体错误信息,帮助用户排查非路径问题的读取故障(如 “权限被拒绝”“文件是二进制而非文本”)。
触发场景

  • 用户试图读取系统保护文件(如 Windows 的C:\Windows\System32\config\SAM),因权限不足无法打开;
  • 用户误将图片文件(如image.png)作为 “原文文件” 输入,读取时因编码不兼容(非 UTF-8)抛出UnicodeDecodeError;
  • 文件因磁盘错误损坏,打开时抛出IOError。
    异常处理核心代码(write_similarity_result函数)
def write_similarity_result(similarity_score, output_path):
try:
    with open(output_path, 'w', encoding='utf-8') as file:
        file.write(f"{similarity_score:.2f}")
except Exception as error:
    print(f"写入文件时出错:{error}")  # 提示具体错误(如“Permission denied”“No space left on device”)
    sys.exit(1)

3.命令行参数数量异常(逻辑判断)

设计目标
当用户输入的命令行参数数量不等于 4 个(正确格式:python main.py [原文] [抄袭文] [输出])时,主动提示正确用法,避免因参数缺失 / 多余导致程序后续读取路径时抛出 “索引越界” 异常。
触发场景
用户忘记输入输出文件路径,命令为python main.py original.txt copied.txt(仅 3 个参数);
用户多输入一个冗余参数,命令为python main.py original.txt copied.txt output.txt extra.txt(5 个参数)。
异常处理核心代码(main函数)

def main():
# 检查命令行参数数量(sys.argv[0]为脚本名,实际参数需3个,总长度4)
if len(sys.argv) != 4:
    print("用法: python main.py [原文文件] [抄袭版论文文件] [答案文件]")  # 清晰提示正确用法
    sys.exit(1)

4. 零向量异常(逻辑判断)

设计目标
当文本预处理后无有效词汇(如文件内容全为标点符号、特殊字符),导致 TF-IDF 向量为 “零向量”(所有维度值均为 0)时,避免余弦相似度计算中出现 “除以零错误“,直接返回相似度0.0。
触发场景
用户输入的文件内容全为标点符号(如 “!@#¥%……&*()”);
文件内容仅含特殊字符,无中英文 / 数字有效词汇。
单元测试样例

def test_edge_case_special_characters(self):
"""测试特殊字符处理:验证零向量异常处理逻辑"""
# 输入全为特殊字符,预处理后无有效词汇
text = "!@#$%^&*()_+{}|:\"<>?[]\\;',./"
processed = preprocess_text(text)
self.assertEqual(processed, [])  # 预处理后为空列表(无有效词汇)

# 计算相似度:两个全特殊字符文本,预期返回0.0
similarity = compute_text_similarity(text, text)
self.assertEqual(similarity, 0.0)

异常处理核心代码(calculate_cosine_similarity函数)

def calculate_cosine_similarity(vector1, vector2):
dot_product = sum(c1 * c2 for c1, c2 in zip(vector1, vector2))
magnitude_vector1 = math.sqrt(sum(c**2 for c in vector1))
magnitude_vector2 = math.sqrt(sum(c**2 for c in vector2))

# 处理零向量:避免除以零错误,返回0.0
if magnitude_vector1 == 0 or magnitude_vector2 == 0:
    return 0.0

return dot_product / (magnitude_vector1 * magnitude_vector2)
posted @ 2025-09-22 20:54  kktl  阅读(22)  评论(0)    收藏  举报