fgets()逐行处理大文件更安全,因内存恒定;file_get_contents()加载超5MB文件易触发内存耗尽错误,且调高memory_limit治标不治本。

大文件用 fgets() 逐行处理,别碰 file_get_contents()
超过 5MB 的文件直接用 file_get_contents() 基本等于触发 Fatal error: Allowed memory size exhausted。这不是代码问题,是 PHP 底层设计决定的——它必须把整个文件加载进内存。你调高 memory_limit 只是掩耳盗铃,反而可能拖垮整个服务。
实操建议:
-
fgets($fp, 8192)显式传长度参数,避免超长行被截断(默认只读 1024 字节) - 读出的每行自带
\n或\r\n,替换后别再额外加换行符,否则空行爆炸 - 写入新文件,处理完再
rename()覆盖原文件,禁止原地fseek()+fwrite()—— 容易破坏字节对齐 - 二进制文件(如 PNG、PDF)完全不能用此法,
fgets()会提前在\0处截断
跨行内容匹配必须用 preg_replace() + s 修饰符
想替换 HTML 注释块、JSON 片段或 XML 标签这种横跨多行的结构,str_replace() 直接失效。因为它的 . 不匹配换行符,而默认正则里的 . 也不匹配 \n。
关键动作只有两个:
- 正则模式末尾加
s修饰符:/(.*?)/s - 用
.*?而非.*,防止贪婪匹配到文件末尾最后一个 - 如果模式里含
/,改用#或~当分隔符,避免转义混乱 -
preg_replace()匹配失败时返回null,不是原字符串,务必先判断再写回
流式处理时如何应对跨块关键词被切开?
当你要用正则替换的内容可能落在两个 fread() 块之间(比如关键词在第 64KB 末尾和第 65KB 开头),硬分块会漏掉匹配。这时得自己维护缓冲区。
核心逻辑:
- 每次
fread($fp, 65536)读 64KB,但预留前一块末尾的至少 100 字节(按关键词最大长度预估) - 用
fseek($fp, $pos - 100, SEEK_SET)回退重读,确保关键词不被切断 - 拼接缓冲区后执行
preg_replace(),只写入已确认不跨块的部分,剩余未闭合内容留到下次循环 - 最后别忘了处理缓冲区残留 —— 循环结束时若
$buffer非空,要单独判断是否构成完整匹配
编码、BOM 和换行符不统一,替换就大概率静默失败
UTF-8 BOM(\xEF\xBB\xBF)开头的文件,或 GBK 编码的配置文件,用默认 str_replace() 替换中文,十有八九找不到目标。PHP 按字节比对,编码错位就全乱。
动手前必须做三件事:
- 用
mb_detect_encoding(file_get_contents($path), ['UTF-8', 'GBK', 'BIG5'], true)探测真实编码 - 读取后统一转成 UTF-8:
$content = mb_convert_encoding($content, 'UTF-8', $detected) - 写回时用
file_put_contents($path, $content, FILE_BINARY),禁用 Windows 下自动换行转换 - 检查文件开头三字节是否为 BOM,是的话替换后要手动补上,否则某些编辑器会识别失败
流式处理时这些步骤得在每行或每块内重复执行,不是只做一次。最易被忽略的是:fgets() 读出的单行也可能含 BOM,尤其首行。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











