PHP用fopen+fgets读文件易卡死或丢行,因文本模式按换行符切分,遇二进制、不规范换行(如单独\r)、超长无换行行会阻塞或返回空;需设超时、用feof辅助判断、显式指定缓冲长度,并注意路径编码与文件编码一致性。

PHP读取文件用 fopen + fgets 容易卡死或丢行?
不是函数本身有问题,而是默认以文本模式打开时,fgets 会按换行符切分,遇到二进制数据、不规范换行(比如 \r 单独出现)、超长无换行的行,就可能阻塞或返回空。尤其读日志、CSV 或用户上传的文本时很常见。
- 务必检查文件编码和换行符:Linux 是
\n,Windows 是\r\n,老 Mac 是\r;fgets对\r处理不稳定 - 加
stream_set_timeout($fp, 5)防卡死,否则网络文件句柄或 NFS 挂载点异常时会无限等待 - 别依赖
fgets的返回值是否为false判定结束——空行、开头就是\n也会返回空字符串,要用feof($fp)辅助判断
为什么 fgets 读大文件慢得离谱?
因为它是逐行缓冲读取,内部每次调用都做内存重分配和字符串拼接,对几百 MB 的日志文件,比 file() 还慢——后者是 C 层一次性 mmap 或 fread,而 fgets 是 PHP 层反复调用。
- 读大文件优先用
file_get_contents()(内存够)或stream_get_line($fp, 8192, "\n")(可控缓冲区) -
fgets($fp, 4096)第二个参数必须显式设,否则默认只读 1024 字节,遇到长行直接截断 - 如果只是统计行数或找某一行,用
foreach (new SplFileObject($path) as $line)更稳,它底层优化了行迭代器
中文路径或 UTF-8 文件名导致 fopen 报错 failed to open stream: No such file or directory
PHP 5.6+ 在 Windows 下对 UTF-8 路径支持仍弱,fopen 接收的是字节流,而你传的 中文.txt 如果是 UTF-8 编码,Windows API 实际需要 GBK 编码的字节序列。
- Windows 上统一用
iconv("UTF-8", "GBK", $path)转路径再传给fopen - Linux/macOS 一般没问题,但若挂载了 NTFS 分区,也要留意文件系统编码设置
- 更稳妥的做法:把文件移到英文路径下操作,或者用
file_put_contents/file_get_contents替代,它们对路径编码容错稍好
fgets 读出来的中文乱码?
不是 PHP 解释错了,是文件本身编码和 PHP 当前脚本编码不一致。比如文件是 GBK 写的,而你的 PHP 文件保存为 UTF-8,fgets 原样返回字节,浏览器或 IDE 就按 UTF-8 解,自然乱码。
立即学习“PHP免费学习笔记(深入)”;
- 先确认文件真实编码:
file -i filename.txt(Linux)或用 VS Code 底部状态栏看 - 读完后立刻转码:
mb_convert_encoding($line, 'UTF-8', 'GBK'),别等最后批量转——中间处理可能出错 - 避免用
setlocale(LC_ALL, ...)试图影响fgets,它完全不走 locale 编码逻辑
fopen 失败没判 false,fgets 返回空没区分是 EOF 还是真空行,还有忘了 fclose($fp) 导致句柄泄露。这些点看着小,线上跑几天就积成故障。










