preg_match()只匹配首次出现即停止,返回0或1;preg_match_all()遍历全文匹配所有,返回总次数并将结果存入多维数组。

preg_match() 和 preg_match_all() 怎么选
看你要“找一个就停”还是“全找出来”。preg_match() 找到第一个匹配就返回 true,后续不继续扫;preg_match_all() 会遍历全文,把所有匹配都塞进结果数组里。
常见错误是用 preg_match() 去验证邮箱或手机号格式——它只检查开头是否能匹配,不保证整个字符串符合。比如 preg_match('/d+/', 'abc123def456') 返回 true,但你其实想确认整串是不是纯数字。
- 要校验完整字符串:在正则前后加
^和$,例如/^d{11}$匹配严格 11 位数字 - 提取多个同类内容(如所有 URL、所有 class 名):必须用
preg_match_all() -
preg_match()的第三个参数$matches是引用传入,不传会警告;preg_match_all()同理,且返回匹配总数(不是布尔值)
修饰符(flags)哪些真正影响行为
PHP 正则默认是“单行模式”,. 不匹配换行符。加 s 修饰符后 . 才能跨行匹配,这个最容易被忽略。
i(忽略大小写)、m(多行模式,让 ^/$ 匹配每行首尾)、u(UTF-8 模式,处理中文/emoji 必加)是高频刚需;x(忽略空白和注释)适合写长正则,但调试时容易因空格误删导致失败。
立即学习“PHP免费学习笔记(深入)”;
- 含中文的字符串,不加
u修饰符,w、、+等可能出错或截断 -
m和s经常被混淆:前者改^/$行为,后者改.行为 - 写完正则先加
u,再根据需求叠加i或s,别省略
捕获组 vs 非捕获组:性能和可读性怎么平衡
用括号 () 默认就是捕获组,PHP 会把内容存进 $matches 数组。但有时你只是想分组逻辑(比如 (ab|cd)ef),并不需要提取 ab 或 cd ——这时该用非捕获组 (?:...)。
大量无用捕获会拖慢速度,尤其在 preg_match_all() 处理长文本时。更隐蔽的问题是:捕获组编号会随括号数量变化,改正则时容易错位引用 $matches[1]、$matches[2]。
- 只做逻辑分组、不需提取内容:一律用
(?:...) - 命名捕获组
(?P<name>...)可读性高,但 PHP 7.3+ 才稳定支持,老环境慎用 - 测试时打印
$matches看结构,比靠编号猜更可靠
PCRE 转义和 PHP 字符串的双重转义陷阱
PHP 字符串本身要解析反斜杠,正则引擎还要再解析一次。所以匹配字面量 d,代码里得写成 '/\\d/' 或更安全的单引号 + 少一层: '/\d/' ——因为单引号中 \ 就是字面 ,传给 PCRE 后变成 d。
最典型翻车场景:想匹配 Windows 路径 C: empile.txt,写成 "/C:\temp\file.txt/",结果报错 PREG_NO_ERROR 却不匹配——因为 被 PHP 当成制表符了。
- 路径、JSON、SQL 片段这类含大量反斜杠的内容,优先用单引号字符串 + 显式双反斜杠:
'/C:\\temp\\file.txt/' - 不确定时,用
addcslashes($str, '\')预处理原始字符串再拼进正则 - 用
var_dump($pattern)确认最终传给 PCRE 的字符串长什么样,比瞎猜快得多
正则越长,边界条件越容易漏。特别是嵌套量词(.*? 和 .* 混用)、Unicode 字符范围(p{Han} 要 u 修饰符)、以及回溯失控(如 (a+)+b 匹配超长 a 字符串时卡死)。这些不是语法问题,而是运行时表现,得靠实际数据压测才能暴露。



















