必须先确认并统一文件编码,再进行替换操作:用file -i或VS Code确认真实编码,读取后立即用mb_convert_encoding转为UTF-8,剥离BOM,归一化换行符,大文件需流式处理,写入前确保编码与目标一致。

替换前必须先确认并统一文件编码
直接用 str_replace 或 preg_replace 处理中文内容却出现乱码,90% 是因为没搞清文件真实编码。PHP 不会自动识别 UTF-8/GBK/BOM,file_get_contents 读出来就是原始字节流。
- 用
file -i filename.txt(Linux/macOS)或 VS Code 右下角编码标识确认真实编码,别信编辑器“显示为 UTF-8” - 读取后立刻转成内部统一编码(推荐 UTF-8):
$content = mb_convert_encoding($content, 'UTF-8', 'GBK');—— 必须显式传入源编码,mb_detect_encoding不可靠,仅作 fallback - 若文件带 BOM(尤其是 UTF-8 with BOM),先剥离:
$content = preg_replace('/^\xEF\xBB\xBF/', '', $content); - 写回前再按需转目标编码(如要保存为 GBK):
$content = mb_convert_encoding($content, 'GBK', 'UTF-8');
多行文本替换时换行符不一致导致失败
Windows 的 \r\n 和 Linux 的 \n 混在一起,str_replace('header\nfooter', ...) 在实际含 \r\n 的文件里根本匹配不上。
- 读取后立即归一化:
$content = str_replace(["\r\n", "\r"], "\n", $content); - 所有替换模板按
\n编写,例如"<!-- config -->\nhost: localhost\n<!-- /config -->" - 跨行正则必须加
s修饰符:preg_replace('/<!-- config -->(.*?)<!-- \/config -->/s', $replacement, $content) - 写回前按目标系统还原:
PHP_OS_FAMILY === 'Windows' ? str_replace("\n", "\r\n", $content) : $content
大文件替换不能全量加载到内存
几百 MB 的日志或 SQL 文件,file_get_contents 直接触发内存溢出(OOM),必须流式处理。
- 用
fopen+fread分块读取,每次处理 8192 字节,避免一次性载入 - 跨块边界可能切开关键词?在块末尾保留最后几十字节缓存,与下一块拼接后再替换
- 写入用
fwrite边处理边写,不缓存整文件 - 更稳妥方案:用
stream_filter_append($fp, 'convert.iconv.UTF-8/GBK')做流式转码,再配合自定义 filter 替换
写入后中文变乱码的常见原因
file_put_contents 是纯字节写入,它不管你是 UTF-8 还是 GBK —— 写什么字节就存什么字节。乱码不是函数问题,是你给它的字符串编码错了。
立即学习“PHP免费学习笔记(深入)”;
- 确保你要写入的字符串,其编码与目标文件期望编码严格一致;错配就是乱码
- 写入前可手动加 UTF-8 BOM:
$content = "\xEF\xBB\xBF" . $content;,方便 Excel/记事本识别 - 权限和 BOM 都可能被覆盖:写入后用
chmod($filename, 0644)保持原权限,BOM 要在转码后、写入前加 - Web 服务器默认字符集(如 Nginx 的
charset utf-8;)只影响 HTTP 响应头,对文件写入无影响 —— 别指望它救你的file_put_contents
实际操作中最容易被跳过的,是「读取后立刻转内部标准编码」这一步。很多人把转码塞在替换逻辑之后,或者只在输出前转一次,结果中间字符串已经损坏,再怎么补救都晚了。



















