第二次作业
| 这个作业属于哪个课程 | 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 打开,中文正常显示:

五、单元测试
使用 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 发现的问题 | 优化方法 | 效果 |
|---|---|---|
代码行统计逐字符扫描,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 对比:

七、总结
- 先定规则再写代码:一开始我以为数单词、数句子很简单,真正动手才发现
don't、3.14、...这些情况都要提前想清楚。把规则写成表格以后,代码和测试都有了依据。 - 测试让修改更放心:项目后期做了很多重构和性能优化,每次改完跑一遍 339 个测试,很快就知道有没有改坏原来的功能。
- 性能优化要靠数据:如果不用 cProfile,我很难猜到最慢的是哪个函数;分析结果直接指出了问题,优化也能用数字说明效果。
- 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 标签。

浙公网安备 33010602011771号