preg_match($pattern, $subject, $matches) 检查字符串是否匹配某个正则表达式。匹配时返回 1,否则返回 0,并把找到的内容放进 $matches:preg_match('/\d+/', 'Order 4521 shipped', $m) 返回 1,并把 $m[0] 设为 "4521"。
正则是一个两边带分隔符的字符串,分隔符通常是 /.../,后面可以跟 i 这样的修饰符。用单引号写正则:在双引号中,PHP 会先展开 $name 和 \x41、\101 这样的转义,正则引擎收到的可能就不是你写的那个正则了。
捕获组
正则中的圆括号会捕获它匹配到的那部分字符串。$m[0] 是整个匹配,$m[1] 是第一个组,$m[2] 是第二个组,按左括号从左往右计数。
命名组
(?<name>...) 给组起一个名字。值会同时存在名字和编号下面,这样即使有人在正则前面加了一个组,读取结果的代码也不会出错。
用 preg_match_all 找出所有匹配
preg_match_all() 在第一个匹配之后会继续查找,并返回匹配的数量。默认情况下,$m[0] 保存所有完整匹配,$m[1] 保存第一个组的所有值。使用 PREG_SET_ORDER 时,每个匹配会得到一个数组,循环处理起来更方便。
校验输入:锚点、电话号码、邮政编码
做校验时,正则必须匹配整个字符串,所以用 ^ 开头,用 $ 结尾。有一个陷阱:$ 也会匹配末尾换行符之前的位置,所以 '/^\d+$/' 会接受 "123\n"。加上 D 修饰符,或者改用只匹配字符串最末尾的 \z。
至于邮箱地址,正则要么会拒绝合法的地址,要么会接受不合法的地址。改用 filter_var($email, FILTER_VALIDATE_EMAIL)(见 filter_var())。
处理 UTF-8、中文和日文的 u 修饰符
没有 u 修饰符时,正则按字节工作。一个中文或日文字符在 UTF-8 中占三个字节,所以 . 只匹配三分之一个字符,算出来的长度也不对。加上 u 后,正则按字符工作,你还可以使用 Unicode 属性类,比如匹配汉字的 \p{Han}。
u 还会校验目标字符串:如果字符串不是合法的 UTF-8,preg_match() 会返回 false,而不是进行匹配。
用 preg_quote 转义用户输入
.、+、?、( 和 / 这样的字符在正则中有特殊含义。如果正则的一部分来自变量,比如搜索词,就用 preg_quote() 处理它,并把分隔符作为第二个参数传入,否则 1+1 会匹配 11,未转义的 / 还会破坏整个正则。
无效的正则不会抛出异常。PHP 会输出一条警告,preg_match() 返回 false:
Warning: preg_match(): No ending delimiter '/' found in /path/to/index.php on line 3
所以当正则是在运行时拼出来的,要检查 === false(或 preg_last_error_msg())。如果只是检查是否包含某个子串,根本不需要正则,str_contains() 更快也更清晰。要修改匹配到的文本,使用 preg_replace()。
常见问题
PHP 中 preg_match 返回什么?
匹配时返回 1,不匹配时返回 0,正则本身无效时返回 false。因为 0 和 false 都是假值,需要区分正则出错和没有匹配时,用 === 1 比较。
怎么从 preg_match 中取出匹配到的文本?
传入第三个参数:preg_match('/(\d+)/', 'Order 4521', $m)。这样 $m[0] 是整个匹配,$m[1] 是第一个捕获组,这里两者都是 "4521"。如果用命名组 (?<id>\d+),值也会存在 $m['id'] 中。
preg_match 和 preg_match_all 有什么区别?
preg_match() 找到第一个匹配就停止。preg_match_all() 会找出所有匹配,返回找到的数量,并把它们全部放进 $matches:preg_match_all('/\d+/', 'a1 b22 c333', $m) 返回 3,$m[0] 是 ["1", "22", "333"]。
怎么用 preg_match 处理日文、中文等 UTF-8 文本?
加上 u 修饰符:preg_match('/^.{3}$/u', '日本語') 返回 1。没有 u 时,PHP 把字符串当作字节处理,所以 . 只匹配一个 3 字节字符中的一个字节。有了 u,你还可以使用 \p{Han}、\p{Hiragana} 和 \p{Katakana} 这样的 Unicode 类。
PHP 中怎么判断字符串是否只包含数字?
用 preg_match('/^\d+$/D', $s),或者不用正则,直接用 ctype_digit($s)。D 修饰符(或用 \z 代替 $)很重要:单独的 $ 也会接受末尾的换行符,所以 "123\n" 也能通过。