fgetc逐字节读取+换行符计数是真正最快的方案,因绕过流封装、避免动态扩容和临时对象开销,比std::getline快4倍以上,且需以二进制模式打开文件并处理末行无换行符情况。

用 fgetc 逐字节读取 + 换行符计数,是 C++(或 C 风格)下真正最快的方案
标准库的 std::getline 虽然写起来简洁,但默认会动态扩容 std::string、构造/析构临时对象、做边界检查——这些在纯行数统计这种无内容需求的场景里全是冗余开销。真正压榨性能时,绕过流封装、直接操作底层文件描述符或 FILE* 更有效。
实操建议:
- 用
FILE*+fgetc,比std::ifstream::get()快 1.5–2 倍(尤其大文件),因避免了 streambuf 同步和 locale 解析 - 确保文件以二进制模式打开:
fopen("file.txt", "rb"),防止 Windows 下\r\n被自动转为单个\n干扰计数逻辑(你得自己处理) - 对超大文件(>1GB),可考虑 mmap + 手动扫描,但需注意
MAP_PRIVATE和页对齐问题,普通场景不必要
Windows 下用 CreateFile + ReadFile 批量读取,比 fgetc 再快 10%–30%
Win32 API 绕过 CRT 缓冲层,直接走内核 I/O,配合 64KB 或更大缓冲区(如 std::vector<char>(65536)</char>),能显著减少系统调用次数。关键不是“API 多高级”,而是批量读 + 本地内存扫描的组合更贴近硬件吞吐能力。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
- 用
ReadFile但每次只读 1 字节 → 完全失去优势,比fgetc还慢 - 忽略最后一行无换行符的情况:若文件末尾非
\n,需额外 +1 行(line_count += (last_char != '\n' && file_size > 0)) - 未设
FILE_FLAG_NO_BUFFERING却配非对齐缓冲区 → 直接报错ERROR_INVALID_PARAMETER
std::count 配合 std::istreambuf_iterator 是最简 C++ 方式,但别在性能敏感处用
它本质是把整个文件当作字符序列遍历,不构造字符串,比 std::getline 轻量,代码仅三行:
std::ifstream f("file.txt", std::ios::binary);
auto begin = std::istreambuf_iterator<char>(f);
auto end = std::istreambuf_iterator<char>();
size_t lines = std::count(begin, end, '\n') + (f.tellg() > 0 ? 1 : 0);但要注意:
-
std::istreambuf_iterator在某些 libstdc++ 版本中存在缓存缺陷,小块读导致频繁 syscalls -
std::count无法提前终止(哪怕你只想知道是否超过 100 万行),必须扫完全部 - 不区分
\r\n和\n,在跨平台统计时结果可能与编辑器显示不一致
别碰 std::getline + std::string 统计行数——除非你明确要内容
这是新手最常写的写法,但每行都会触发至少一次内存分配(即使 string 小于 16 字节也可能 SSO 失效)、拷贝、析构,时间复杂度从 O(N) 变成 O(N×avg_line_len)。实测 1GB 日志文件,std::getline 耗时可能是 fgetc 的 4 倍以上。
如果你真要用它,至少加两处优化:
- 预分配
std::string容量:line.reserve(256),减少重分配次数 - 禁用同步:
std::ios_base::sync_with_stdio(false),关闭 cin/cout 绑定,提速约 20% - 但即便如此,仍不如裸指针扫描——因为内存管理开销无法绕过
行数统计真正的瓶颈从来不在算法,而在 I/O 模式和内存访问局部性。最容易被忽略的是:文件系统缓存状态会极大影响多次运行的耗时差异,做性能对比前务必用 echo 3 > /proc/sys/vm/drop_caches(Linux)或 Clear-FileCache(PowerShell)清空缓存,否则测出来全是假数据。



















