第二次作业

这个作业属于哪个课程 26软件技术基础
这个作业要求在哪里 WordCount 命令行文本计数统计程序
这个作业的目标 实现命令行文本计数统计程序,使用 Git 进行版本管理,编写单元测试并进行性能测试
姓名-学号 陈金怀-2024329301036
码云仓库地址 https://gitee.com/cjh0716/word-count
程序下载 v0.3 发行版(wc.exe、wcgui.exe,无需安装 Python)

一、项目简介

本次作业实现了一个文本计数统计程序 WordCount,命令格式为:

wc.exe [参数] [文件名]

我电脑上的开发环境是 Python,所以程序用 Python 编写,再用 PyInstaller 打包成独立的 wc.exe,没有安装 Python 的电脑也能直接运行。

类别 内容
基础功能 统计纯英文 txt 文本的字符数(-c)、单词数(-w)、句子数(-s)
扩展功能 统计源代码的代码行、空行、注释行(-x)和总行数(-l),支持 C/Java/JS、Python、Shell、HTML、CSS、SQL、Lua 的注释语法
自行增加的功能 段落数、平均词长/句长、单词频率;递归统计目录;表格/JSON/CSV 输出;管道输入、交互模式、配置文件、中文错误提示;图形界面
测试 339 个单元测试(含基于随机输入的属性测试),代码覆盖率 98%
性能 用 cProfile 找出瓶颈并优化;大文件分块读取,50 MB 文件峰值内存从 573 MB 降到 23 MB;多进程并行统计

二、版本迭代

按照作业要求,项目通过三次提交体现版本更迭,每个版本都打了 Git 标签:

版本 内容
v0.1 空项目:搭建目录结构、.gitignore、README
v0.2 基础功能:-c 字符数、-w 单词数、-s 句子数;48 个单元测试
v0.3 扩展功能与全部加分项:代码行/空行/注释行、更多功能、339 个单元测试、性能测试与优化

提交记录


三、设计与实现

1. 项目结构

wc.py / wcgui.py      命令行和图形界面的入口
wordcount/
  counter.py          字符、单词、句子、段落、单词频率的统计
  linestats.py        代码行、空行、注释行的统计(多语言注释识别)
  analyzer.py         统计项目注册表,按需计算、分块累加
  report.py           读取文件、识别编码、多进程并行,生成统计报告
  files.py            通配符、目录递归、文件过滤
  formatters.py       表格 / JSON / CSV 输出
  cli.py              命令行参数解析
  interactive.py      交互模式
  gui.py              图形界面
tests/                单元测试
perf/                 性能测试

统计逻辑(counter.py、linestats.py)和输入输出(cli.py、gui.py)完全分开,命令行和图形界面调用的是同一套统计代码,结果一定一致,统计函数也可以单独测试。

2. 统计规则

写代码之前,我先把“字符、单词、句子”的规则定义清楚,再按规则编写代码和测试:

项目 规则
字符 所有字符,包括空格、标点和换行;Windows 的 \r\n 与 \n 一样算 1 个字符
单词 由字母/数字组成的连续串;don't、well-known 各算 1 个;3.14、1,000 算 1 个
句子 以 . ! ? 结尾;连续的 ... ?! 只算一次;3.14 中的小数点不算句末;最后一句没有标点也算
空行 只有空白,或只有一个可见字符(如单独的 { })
注释行 含有注释,且注释外最多一个可见字符,如 } // end;块注释覆盖的每一行都算
代码行 其余的行;字符串中的 //、/* 不算注释

3. 关键实现

单词和句子用正则表达式实现:

WORD_RE = re.compile(r"\d+(?:[.,]\d+)+|[A-Za-z0-9]+(?:['’-][A-Za-z0-9]+)*")
SENTENCE_END_RE = re.compile(r"[.!?]+[\"'”’)\]]*(?=\s|$)")

句子的结束标记要求后面是空白或文本末尾,所以 3.14、example.com 中的点不会被当成句末。

注释行的难点是区分“真正的注释”和“字符串里的注释符号”。程序逐行扫描,用一个正则表达式直接跳到下一个注释符号或引号:遇到引号就跳过整个字符串(处理 \" 转义),遇到 /* 就进入块注释状态,直到找到 */;这个状态会跨行保存。Python 中单独成句的三引号字符串会被识别为文档字符串(算注释行),而赋值给变量的三引号字符串算代码行。


四、运行示例

1. 基础功能:字符数、单词数、句子数

示例文件 sample.txt:

The quick brown fox jumps over the lazy dog. It's a well-known sentence!
Did you know it uses every letter of the alphabet? Yes, it does...
Pi is about 3.14 and that is fine

基础功能

  • 字符数 174:3 行共 171 个字符,加 3 个换行符;
  • 单词数 34:It's、well-known、3.14 各算 1 个;
  • 句子数 5:第 1、2 行各两句(... 只算一次),第 3 行没有结尾标点也算 1 句,3.14 的小数点不算句末。

2. 扩展功能:代码行、空行、注释行

扩展功能

以 sample.c 为例:块注释、// 注释、} // end of main 都算注释行(共 6 行);空白行和只有 { } 的行算空行(共 6 行);
带行尾注释的 printf(...); // ... 仍然是代码行,字符串 "// not a comment" 里的 // 也不会被当成注释。
最后一条命令用 -r 递归统计了本项目自己的源代码。

3. 文本分析与输出格式

文本分析

4. 管道、错误提示与交互模式

参数或文件名写错时,程序会用中文提示,并给出最接近的正确写法;交互模式中可以连续输入命令。

易用性

5. 图形界面

为不熟悉命令行的同学做了一个图形界面(tkinter),可以添加文件/文件夹或直接粘贴文本,勾选统计项目,查看结果表格和单词频率柱状图,并导出为 CSV/JSON:

图形界面

导出的 CSV 用 Excel 打开,中文正常显示:

导出到 Excel


五、单元测试

使用 pytest 编写,共 339 个测试用例,代码覆盖率 98%:

单元测试

测试文件 测试内容
test_counter.py 基础功能:空文本、多空格/换行/Tab、撇号与连字符、数字与小数、CRLF 换行、连续标点、引号括号结尾、无结尾标点
test_cli.py 基础功能:各参数及组合、多文件、通配符、文件不存在、非法参数、UTF-8/BOM/GBK 编码、输出格式
test_linestats.py 扩展功能:空行/代码行/注释行判定、块注释、字符串中的注释符号、转义引号、Python 文档字符串、HTML/SQL/Lua
test_files.py 等 目录递归、文件过滤、交互模式、配置文件、错误提示、图形界面
test_streaming.py 分块读取与整体读取结果一致、多进程与单进程结果一致
test_properties.py 属性测试:用 Hypothesis 自动生成大量随机文本,验证“每行恰好是代码行/空行/注释行之一”“\n 与 \r\n 结果相同”等规律

属性测试很有用:普通测试只能检查我想到的例子,而属性测试会自动生成各种奇怪的输入,帮我确认规则在任何情况下都成立。


六、性能测试

使用 VS Code 和 Python 自带的性能分析工具 cProfile(在 VS Code 中配置了一键运行的任务)。测试脚本生成 10 MB 的英文文本和 10 MB 的 C 代码,统计每个函数的调用次数和耗时:

cProfile 分析

根据分析结果做了以下优化:

cProfile 发现的问题 优化方法 效果
代码行统计逐字符扫描,str.startswith 等被调用数千万次 用正则表达式直接跳到下一个注释符号或引号 1.72 s → 0.96 s
大多数代码行不含注释符号,却仍进入完整扫描 增加快速路径,先判断有没有特殊符号 0.96 s → 0.77 s
整个文件一次读入内存 按 256 KB 分块读取,块注释、句子等状态跨块保存 50 MB 文件峰值内存 573 MB → 23 MB
统计大量文件时只用一个 CPU 核 -j 参数多进程并行 1000 个文件 1.67 s → 0.62 s

最后用基准测试脚本,在独立进程中测量完整程序的耗时和峰值内存,与 v0.2 对比:

基准测试


七、总结

  1. 先定规则再写代码:一开始我以为数单词、数句子很简单,真正动手才发现 don't、3.14、... 这些情况都要提前想清楚。把规则写成表格以后,代码和测试都有了依据。
  2. 测试让修改更放心:项目后期做了很多重构和性能优化,每次改完跑一遍 339 个测试,很快就知道有没有改坏原来的功能。
  3. 性能优化要靠数据:如果不用 cProfile,我很难猜到最慢的是哪个函数;分析结果直接指出了问题,优化也能用数字说明效果。
  4. Git 版本管理:通过这次作业熟悉了提交、打标签、推送到码云和发布版本的完整流程。

八、AI 对话提示词

本项目使用 AI 编程助手(Claude Code)辅助完成。下面是开发过程中按顺序使用的全部提示词,仓库中的 AI_PROMPTS.md 也有同样的内容。

一、v0.1 空项目

提示词 1:搭建项目结构

我要完成一个软件工程作业:用命令行实现文本计数统计程序 wc.exe,能统计纯英文 txt 文本的字符数、单词数、句子数,
命令格式为 wc.exe [参数] [文件名],例如 wc.exe -c file.txt 统计字符数、wc.exe -w file.txt 统计单词数。
扩展功能是统计代码行、空行、注释行。项目要上传码云,并按 v0.1 空项目、v0.2 基础功能、v0.3 扩展功能三个版本提交。

我电脑上只有 Python 环境,请用 Python 实现,最后用 PyInstaller 打包成 wc.exe。
请先帮我搭建 v0.1 空项目:
1. 设计清晰的目录结构:程序入口 wc.py、核心代码放在 wordcount 包里、测试放在 tests 目录;
2. 写好 .gitignore(忽略 __pycache__、build、dist 等);
3. README.md 先写项目简介和计划实现的功能;
4. 初始化 git 仓库并完成第一次提交,打上 v0.1 标签。

二、v0.2 基础功能

提示词 2:明确统计规则

在写代码之前,请先帮我把“字符、单词、句子”的统计规则定义清楚,并写进代码注释和 README:
- 字符:是否包含空格和换行?Windows 的 \r\n 应该算 1 个还是 2 个字符?
- 单词:don't、well-known 这类带撇号、连字符的词怎么算?3.14、1,000 这样的数字算几个单词?
- 句子:以 . ! ? 结尾;连续标点 "..." "?!" 只算一次;3.14 中的小数点不能当成句末;
  最后一句没有结尾标点时也要算;只有标点没有单词的不算句子。
规则要合理、能自圆其说,后面的单元测试按这些规则编写。

提示词 3:实现基础功能

按上面的规则实现基础功能:
1. wordcount/counter.py 实现 count_chars、count_words、count_sentences 三个函数,用正则表达式实现,代码简洁;
2. wordcount/cli.py 用 argparse 实现命令行:-c 字符数、-w 单词数、-s 句子数,参数可以组合,
   不加参数时默认全部输出,支持一次统计多个文件,-h 显示中文帮助,-v 显示版本;
3. 文件不存在时给出中文错误提示并继续处理其他文件,程序返回非 0 退出码;
4. 读取文件时自动识别 UTF-8(含 BOM)和 GBK 编码,避免中文 Windows 上读文件报错;
5. 在 Windows 的 cmd、PowerShell、Git Bash 里输出中文都不能乱码。

提示词 4:基础功能的单元测试

用 pytest 为基础功能编写单元测试,要覆盖:
空文本、只有空白、多个空格和换行、Tab、标点与单词相连、撇号和连字符、数字和小数、
CRLF 换行、连续标点、引号和括号结尾的句子、没有结尾标点的句子;
命令行部分测试各参数、参数组合顺序、多文件、文件不存在、非法参数、各种编码、程序入口。
测试全部通过后,更新 README(简介、用法、文件列表、运行示例),提交并打上 v0.2 标签。

三、v0.3 扩展功能与加分项

提示词 5:代码行 / 空行 / 注释行

实现作业要求的扩展功能:统计代码行、空行、注释行,用 -x 参数开启,另加 -l 统计总行数。
判定规则参考经典 WordCount 作业:
- 空行:只有空白字符,或只有一个可见字符(如单独的 { 或 });
- 注释行:含有注释,且注释外最多一个可见字符(如 } // end);块注释覆盖的每一行都算注释行;
- 代码行:其余的行。
要注意字符串里的 // 和 /* 不是注释,转义引号 \" 不能结束字符串。
按扩展名识别注释语法:C/C++/Java/JS 用 // 和 /* */,Python 用 # 和文档字符串,
Shell/YAML 用 #,HTML/XML 用 <!-- -->,SQL/Lua 用 --;同时提供 --lang 参数手动指定。
Python 中单独成句的三引号字符串算注释(文档字符串),赋值给变量的三引号字符串算代码。

提示词 6:批量统计与结构化输出

让程序更适合统计一个真实的项目:
1. 文件名支持通配符(cmd 不会自动展开 *.txt,需要程序自己展开);
2. -r 递归统计整个目录,--include / --exclude 按文件名过滤,自动跳过 .git、node_modules 等目录和二进制文件;
3. 默认输出对齐的表格(注意中文字符占两列宽),多个文件时最后一行显示合计;
4. --format 支持 table / plain / json / csv 四种格式,-o 把结果保存到文件,CSV 要能直接用 Excel 打开不乱码;
5. 平均值类的统计项,合计行要按总数重新计算,不能把各文件的平均值相加。

提示词 7:文本分析

在字符、单词、句子之外再增加几项有实际意义的文本分析:
-p 段落数(空行分隔)、-m 平均词长和平均句长、-f 单词频率(不区分大小写,-n 指定显示前几个,
--stopwords 忽略 the、a、is 等停用词),表格模式下用字符柱状图直观显示频率,-a 输出全部统计项。

提示词 8:易用性

提升程序的易用性:
1. 不写文件名时从标准输入读取,支持管道,如 type file.txt | wc.exe -w;
2. -i 进入交互模式,可以反复输入命令,支持 text(直接统计一段文本)、set(设置默认参数)、cd、ls、help、quit;
   参数写错时只提示错误、不退出交互模式;
3. 支持 .wcrc 配置文件写默认参数,命令行参数优先,--no-config 临时不用;
4. 错误提示全部改成中文,参数或文件名拼错时提示最接近的正确写法,例如“你是不是想用 --words?”。

提示词 9:图形界面

为不熟悉命令行的同学做一个图形界面,用 Python 自带的 tkinter,不增加第三方依赖:
可以添加文件和文件夹,或直接粘贴英文文本;勾选统计项目;表格显示结果和合计行;
右侧画单词频率柱状图;可以导出 CSV / JSON。统计放在后台线程,窗口不能卡住。
统计逻辑必须和命令行共用同一套代码,不能重复实现。用 wc.exe --gui 或单独的 wcgui.exe 启动。

提示词 10:用 IDE 工具做性能测试并优化

用 IDE(VS Code)附带的工具做 performance test:
1. 写性能测试脚本,生成几十 MB 的英文文本和 C 代码,用 cProfile 分析各函数耗时,找出瓶颈并优化;
2. 大文件改为分块读取,避免一次把几百 MB 的文件读进内存;分块统计的结果必须和整体统计完全一致,
   要特别注意跨块的单词、句子、多行注释、\r\n 和多字节字符;
3. 统计大量文件时支持 -j 参数多进程并行;
4. 写基准测试脚本,在独立进程中测量耗时和峰值内存,与 v0.2 版本对比,结果写进 README;
5. 在 .vscode 中配置好运行、调试、测试和性能分析任务,方便在 IDE 中一键运行。

提示词 11:更多测试用例与工程化

完善测试和工程化,让项目达到可以交付的质量:
1. 为所有新功能补充单元测试,并用 Hypothesis 做基于随机输入的属性测试
   (例如:每一行恰好是代码行、空行、注释行之一;\n 和 \r\n 换行的统计结果相同;分块统计等于整体统计);
2. 用 coverage 统计测试覆盖率,用 flake8 检查代码规范,修复所有问题;
3. 配置 GitHub Actions 持续集成,在 Windows 和 Linux 上自动运行测试;
4. 写 CHANGELOG.md;README 补充统计规则、运行示例和截图、测试与性能测试结果;
5. 用真实的命令行窗口和图形界面截图(不能是手工编的输出),放到 docs/images 中;
6. 打包最终的 wc.exe 和 wcgui.exe,提交并打上 v0.3 标签。
posted @ 2026-10-25 16:23  Cjh0716  阅读(4)  评论(0)    收藏  举报