fgets() 逐行读大文件本身不致内存溢出,问题在于后续操作;需单行处理、及时释放、避免字符串拼接、显式设长度、配合资源清理及监控,或改用 SplFileObject。

用 fgets() 逐行读取大文件本身不会导致内存溢出,真正引发问题的是后续操作——比如把所有行存进数组、反复拼接字符串、或在循环里不做释放就持续累积数据。关键不是 fgets 本身,而是你“怎么用”它。
只留一行,用完即弃
每次调用 fgets() 返回的是一行字符串,它只占用当前这一行的内存。只要你不把它长期保存,内存就不会滚雪球增长。
- 处理完立刻丢掉:解析、写库、输出后不赋值给变量,或显式设为
null - 避免
$all .= $line这类追加拼接,哪怕只做几十万次,PHP 也会不断复制字符串副本 - 需要临时组合字段时,优先用
sprintf()或implode()一次性生成,而不是循环中多次.=
注意 fgets 的默认长度陷阱
fgets() 默认最多读 1024 字节,超长行会被截断——这不仅影响数据完整性,还可能让下一次读取错位,间接导致逻辑错误甚至死循环。
- 务必显式传入足够大的长度参数,例如
fgets($fp, 8192) - 若不确定最长行长度,可结合
stream_get_line()或fread()分块读取并手动按换行符切分 - 对 UTF-8 文件,注意避免在多字节字符中间截断,必要时用
mb_substr()辅助校验
配合资源清理与监控
即使单行处理很轻量,长时间运行仍可能因 PHP 引用计数延迟、未关闭句柄或缓存残留引发隐性内存增长。
立即学习“PHP免费学习笔记(深入)”;
- 处理完及时
fclose($fp),别依赖脚本结束自动释放 - 在循环中定期调用
gc_collect_cycles()(尤其处理 JSON 或对象时) - 用
memory_get_usage(true)每处理 N 行检查一次,发现异常上升立即排查 - 禁用输出缓冲:
ob_end_flush()或ini_set('output_buffering', 'Off'),防止 echo 输出滞留在内存
替代方案:SplFileObject 更稳更简洁
如果你不需要底层控制,SplFileObject 是 fgets() 的面向对象升级版,自带迭代器接口、自动跳过空行、支持 seek 定位,且内部做了更稳妥的内存管理。
- 写法更干净:
foreach (new SplFileObject($file) as $line) { ... } - 天然支持
current()、key()、next(),适合需随机访问行号的场景 - 比裸用 fgets() 少写几行错误处理,也不容易漏掉
fclose



















