用 find + grep 可快速统计 C++ 项目有效代码行与注释行:只扫描 .cpp/.h/.hpp,排除 build/、third_party/ 等目录;用 grep -v "^\s$" 过滤空行;用 grep "^\s//" 统计单行注释行;块注释难以精确统计,通常忽略。

用 find + grep 快速统计 C++ 项目总行数和注释行
Linux/macOS 下不用写脚本也能干这事,关键是别把空行、头文件、第三方代码混进去。默认的 wc -l 统计太糙,会把 #include 行、空行、甚至二进制文件都算上。
- 只扫
.cpp和.h(或.hpp),跳过build/、vendor/、third_party/ - 用
grep -v "^$"排除纯空行;用grep -v "^[[:space:]]*$"更稳妥(含空格的空行) - 注释行得区分:单行注释
//要整行匹配,块注释/* ... */很难靠 grep 精确统计,所以通常只算以//开头的行 - 命令示例:
find . -name "*.cpp" -o -name "*.h" -o -name "*.hpp" | grep -v "/build/" | grep -v "/third_party/" | xargs cat | grep -v "^[[:space:]]*$" | wc -l
这是有效代码行(非空、非注释);再补一句find . -name "*.cpp" -o -name "*.h" -o -name "*.hpp" | grep -v "/build/" | grep -v "/third_party/" | xargs cat | grep "^[[:space:]]*//"
就能拿到注释行
Windows 上用 PowerShell 替代 find + grep
PowerShell 没有内置 grep,但 Select-String 能干类似的事,只是默认行为容易漏匹配——比如不识别 ^ 行首锚点,得加 -Pattern 和 -CaseSensitive 才可靠。
- 先获取所有源文件:
Get-ChildItem -Recurse -Include "*.cpp","*.h","*.hpp" | Where-Object { $_.FullName -notmatch "build|third_party|vendor" } | ForEach-Object { Get-Content $_.FullName } - 过滤空行:
| Where-Object { $_ -match "\S" }(只要含非空白字符就算) - 统计注释行:
| Select-String "^\s*//" | Measure-Object;注意这里^\s*在 PowerShell 中需用-Pattern显式启用正则,否则^当字面量处理 - 别用
Get-Content直接拼大字符串——大项目会爆内存,建议用ForEach-Object流式处理
clang 预处理器输出能帮你绕过宏干扰吗?
不能。预处理后的文件里,#define 展开、#ifdef 消除确实让逻辑更“干净”,但注释早被预处理器删光了——clang -E 输出里根本没 // 或 /*,连行号都重排过。想统计原始注释量,必须读源码,不是读预处理结果。
- 误用示例:
clang -E *.cpp | grep "//" | wc -l
——这几乎永远返回 0 - 如果你真关心“实际参与编译的代码行”,那应该看
clang -fshow-source-location日志,但那是调试用的,不适用于常规统计 - 宏定义本身占行数,但不属于“可读代码”;若要排除宏,只能靠词法分析器(如
libclang),已超出“小脚本”范畴
Python 小脚本比 shell 更可控,但要注意编码和 BOM
写个 20 行 Python 脚本确实更稳,尤其跨平台时。但 Windows 上很多编辑器保存 .cpp 文件会带 UTF-8 BOM,open() 默认读出来开头是 \ufeff,导致第一行匹配失败——比如 // 注释被当成 \ufeff//,startswith("//") 就不成立。
立即学习“C++免费学习笔记(深入)”;
- 打开文件必须显式指定
encoding="utf-8-sig",自动剥离 BOM - 别用
readlines()加strip()判空——strip()会吃掉行尾换行符,影响后续判断;改用line.strip() == ""更安全 - 注释判断别只写
line.startswith("//"),要加空格容忍:line.strip().startswith("//"),否则缩进后的// comment就漏了 - 路径遍历用
pathlib.Path.rglob,比os.walk清爽,且自带match()过滤
C++ 注释统计真正的难点不在工具链,而在怎么定义“一行注释”——是只要含 // 就算,还是必须整行都是注释?/<em> ... </em>/ 跨多行时,首尾行算不算?这些边界没统一标准,脚本就只能按最简规则来,别指望它替你做语义判断。










