preg_replace 默认子串匹配,需用\b锚定单词边界;中文需Unicode适配;保留缩进要捕获空白;大文件应流式处理;换行问题源于I/O模式而非正则。

preg_replace 为什么总把“cat”替成“category”里的部分?
因为没加单词边界 \b,正则默认做子串匹配,不是词义匹配。比如 /cat/ 会命中 category、scatter 甚至 scat 里的 cat。
正确写法必须锚定边界:/\bcat\b/。它只匹配独立单词,前后都必须是单词边界(字母/数字/下划线以外的字符,或字符串起止)。
- 想忽略大小写?加
i修饰符:/\bcat\b/i - 要匹配带连字符的词,比如
self-contained,\b会在连字符处断开,此时改用(?<=^|[^a-zA-Z0-9_])cat(?=$|[^a-zA-Z0-9_])/ - 中文场景下
\b不可靠(因 UTF-8 字符不被识别为“单词字符”),应改用(?<=[^\w\u4e00-\u9fa5])cat(?=[^\w\u4e00-\u9fa5]|$)或直接用/\bcat\b/u+mb_internal_encoding('UTF-8')
替换后缩进和空格全乱了,怎么保留上下文?
直接 preg_replace('/old/', 'new', $text) 会吃掉前后空白——正则引擎不自动保留捕获范围外的字符。尤其当 old 在行首或嵌套缩进里时,new 会贴着行首或上一行末尾输出。
解决办法是把需要保留的空白显式捕获并回填:
立即学习“PHP免费学习笔记(深入)”;
- 行首缩进:用
/^(\s*)old(\s*)$/m,替换为$1new$2,m修饰符让^和$匹配每行起止 - 避免
.跨行失效:它不匹配换行符,导致多行结构断裂;改用[\s\S]或(?s). - 如果目标是“替换某行中某个词但不动其他列”,建议先用
file($path, FILE_IGNORE_NEW_LINES)按行读,逐行处理再拼回,比全文正则更可控
大文件里用 preg_replace 替换,内存爆了怎么办?
file_get_contents 把整个文件读进内存,50MB 以上就容易 OOM。这不是正则的问题,是加载策略问题。
流式处理可行但有硬限制:正则无法跨 chunk 匹配(比如 /start.*?end/s 若被切在中间就失效)。实际能做的妥协只有:
- 按固定长度分块(如 8KB),但只处理明确不跨块的模式,例如单行配置项:
/^\s*host\s*=>\s*['"]([^'"]*)['"]/m - 用
fopen+fgets逐行读,对每行做preg_replace,适用于换行符语义清晰的文本(如 ini、php 数组、JSON 行格式) - 真正要跨行匹配的大文件(如日志聚合),放弃正则,改用状态机或专用解析器(如
json_decode处理 JSON,parse_ini_file处理 ini)
替换完写回文件,换行符全变成 \n 了?
这是 file_put_contents 在 Windows 下默认启用文本模式,把所有 \n 转成 \r\n,而 Linux/macOS 又可能反向转换——根源不在正则,而在 I/O 模式。
关键动作只有两个:
- 读取时别用
file(),它会自动 strip\r\n;改用fopen+fgets或file_get_contents(后者原样返回字节流) - 写入时**必须加
FILE_BINARY标志**:file_put_contents($path, $content, FILE_BINARY | LOCK_EX),禁用任何换行符转换 - 若需跨平台统一换行,先归一化:
str_replace(["\r\n", "\r"], "\n", $content),再按目标系统补回:str_replace("\n", PHP_EOL, $content)
正则本身不碰换行符,但它的输入输出链路会受 I/O 层干扰——这点最容易被忽略,一出问题就往 preg_replace 上猜。



















