最直接但有内存风险的方法是file()配合count();推荐splfileobject跳转末尾取key()+1;exec('wc -l')最快但需注意安全与兼容性;若只需判断行数是否超限,应提前退出避免全量读取。

用 file() 读取后取 count() 最直接但有内存风险
多数人第一反应是 file('path.txt') 加 count(),确实能快速得到行数:
$lines = count(file('data.log'));但它会把整个文件载入内存。遇到几百 MB 的日志,PHP 进程可能直接 OOM,尤其在低配服务器或 memory_limit 较小的环境(比如默认 128M)。
适用场景:小文件(
- 注意
file()默认保留换行符,但不影响count()结果 - 若文件末尾无换行符,最后一行仍会被计入——这是预期行为,不是 bug
- Windows 和 Unix 换行符(
\r\nvs\n)对结果无影响,file()自动按 PHP 的auto_detect_line_endings处理
逐行读取 + 计数更省内存,适合大文件
用 fopen() + fgets() 或 SplFileObject 避免一次性加载,行数统计可控在几 KB 内存开销:
$fp = fopen('bigfile.csv', 'r');<br>$lineCount = 0;<br>while (fgets($fp) !== false) {<br> $lineCount++;<br>}<br>fclose($fp);
更简洁的写法(PHP 5.6+):
$file = new SplFileObject('access.log');<br>$file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);<br>$file->seek(PHP_INT_MAX); // 快速跳到末尾<br>$lineCount = $file->key() + 1;这个技巧利用了 SplFileObject::key() 返回当前行号(从 0 开始),seek(PHP_INT_MAX) 会定位到最后一个有效行。
立即学习“PHP免费学习笔记(深入)”;
-
SplFileObject::SKIP_EMPTY能跳过空行,如需包含空行请去掉该 flag - 如果文件以空行结尾,
key() + 1仍准确;但若文件为空,seek(PHP_INT_MAX)会抛出异常,需加try/catch - 比
exec('wc -l')更跨平台,不依赖 shell 命令,也避免了安全风险(如文件名含空格或特殊字符)
用 exec('wc -l') 最快但要注意安全和兼容性
在 Linux/macOS 上,系统 wc -l 是 C 实现,处理 GB 级文件只要毫秒级:
$path = escapeshellarg('/var/log/syslog');<br>$output = exec("wc -l $path 2>/dev/null", $out, $return);<br>$lineCount = (int)trim($output);
关键点在于必须用 escapeshellarg() 处理路径,否则含空格、单引号、$ 符号的路径会导致命令注入或失败。
- Windows 不原生支持
wc,除非装了 Git Bash 或 WSL,否则会失败——不能无脑用 -
exec()可能被禁用(disable_functions包含exec),上线前务必检查phpinfo() - 返回值为字符串,需
(int)强转,否则可能混入警告信息(如权限不足时输出 “Permission denied”)
判断“行数是否在某范围内”别先算总数再比较
如果真实需求只是“是否超过 1000 行”,没必要统计全部——提前退出能极大提升响应速度:
$limit = 1000;<br>$fp = fopen($file, 'r');<br>for ($i = 0; $i <= $limit && fgets($fp) !== false; $i++);<br>$isOverLimit = ($i > $limit);<br>fclose($fp);
这段代码在读到第 1001 行时就停,哪怕文件有 100 万行,也只读前 1001 行。适用于上传校验、预检、分页计数等场景。
- 注意循环条件是
$i ,因为 <code>$i从 0 开始,第 1001 次迭代对应第 1001 行 - 如果文件实际只有 500 行,
fgets()最终返回false,循环自然结束,$i停在 500,$isOverLimit为false - 这种写法对超大文件友好,但无法得知精确总数——如果后续还需总数,就得重开文件再扫一遍
escapeshellarg() 或忽略空文件边界,线上就容易出错。











