正则表达式(Regular Expression,简称regex)是处理字符串的强大工具。它用一套特殊的语法,描述字符串的模式,可以用来匹配、查找、替换、分割字符串。
很多开发者觉得正则表达式难学、难记、难调试,看到一串密密麻麻的符号就头疼。但实际上,正则表达式的语法并不复杂,掌握了基本规则,就能写出大多数常用的正则。而且正则表达式在开发中非常常用——表单验证、字符串提取、文本替换、数据清洗、URL路由……几乎所有涉及字符串处理的地方,都能用到正则。
作为PHP开发者,正则表达式是必备技能。PHP提供了强大的PCRE(Perl Compatible Regular Expressions)扩展,支持完整的正则表达式功能。今天分享PHP正则表达式的实战指南,从基本语法到常用模式,帮你掌握正则这个利器。
正则表达式基础
什么是正则表达式
正则表达式是一种描述字符串模式的语言。它由普通字符和特殊字符(元字符)组成,用来匹配符合某种模式的字符串。
比如,正则表达式/abc/可以匹配字符串中包含"abc"的部分;/[0-9]+/可以匹配一个或多个数字;/^\w+@\w+\.\w+$/可以匹配邮箱地址。
PHP中的正则函数
PHP的PCRE扩展提供了以下常用函数:
preg_match($pattern, $subject, $matches):执行一次匹配,返回匹配次数(0或1)pregmatchall($pattern, $subject, $matches):执行全局匹配,返回匹配次数preg_replace($pattern, $replacement, $subject):执行正则替换preg_split($pattern, $subject):用正则分割字符串preg_grep($pattern, $array):用正则过滤数组preg_quote($str):转义正则特殊字符
基本语法
定界符
PHP的正则表达式需要用定界符包裹,常用的定界符是/,也可以用#、~等。当正则中包含/时,用其他定界符可以避免转义。
preg_match('/abc/', $str); // 用/作为定界符
preg_match('#http://#', $str); // 用#作为定界符,避免转义/修饰符
定界符后面可以加修饰符,改变正则的行为:
i:不区分大小写(case-insensitive)m:多行模式,^和$匹配每行的开头和结尾s:点号模式,.匹配包括换行符在内的所有字符x:忽略空白和注释u:UTF-8模式,处理多字节字符
preg_match('/abc/i', 'ABC'); // 匹配,不区分大小写普通字符
字母、数字、汉字等普通字符,匹配它们本身。
preg_match('/hello/', 'hello world'); // 匹配hello元字符
元字符是正则表达式中具有特殊含义的字符:
| 元字符 | 含义 |
|---|---|
. | 匹配除换行符外的任意单个字符 |
^ | 匹配字符串开头(多行模式下匹配行首) |
$ | 匹配字符串结尾(多行模式下匹配行尾) |
* | 匹配前面的元素0次或多次 |
+ | 匹配前面的元素1次或多次 |
? | 匹配前面的元素0次或1次(可选) |
{n} | 匹配前面的元素恰好n次 |
{n,} | 匹配前面的元素至少n次 |
{n,m} | 匹配前面的元素n到m次 |
[] | 字符类,匹配方括号中的任意一个字符 |
[^] | 否定字符类,匹配不在方括号中的任意字符 |
() | 分组,将括号内的表达式作为一个整体 |
| | 或,匹配左边或右边的表达式 |
\ | 转义字符,将特殊字符转义为普通字符 |
字符类
[]表示字符类,匹配其中的任意一个字符:
[abc]:匹配a、b或c[a-z]:匹配a到z的任意小写字母[A-Z]:匹配A到Z的任意大写字母[0-9]:匹配0到9的任意数字[a-zA-Z0-9]:匹配字母或数字[^abc]:匹配除a、b、c外的任意字符
预定义字符类
正则提供了一些预定义的字符类,方便使用:
\d:匹配数字,等价于[0-9]\D:匹配非数字,等价于[^0-9]\w:匹配单词字符(字母、数字、下划线),等价于[a-zA-Z0-9_]\W:匹配非单词字符\s:匹配空白字符(空格、制表符、换行符等)\S:匹配非空白字符\b:匹配单词边界\B:匹配非单词边界
量词
量词指定前面的元素重复多少次:
*:0次或多次(贪婪)+:1次或多次(贪婪)?:0次或1次(贪婪){n}:恰好n次{n,}:至少n次{n,m}:n到m次
贪婪与非贪婪
默认情况下,量词是贪婪的——会尽可能多地匹配。在量词后面加?,可以变成非贪婪(懒惰)模式——尽可能少地匹配。
$str = '<a>hello</a><a>world</a>';
preg_match('/<a>.*<\/a>/', $str, $m); // 贪婪,匹配整个字符串
preg_match('/<a>.*?<\/a>/', $str, $m); // 非贪婪,只匹配第一个<a>...</a>分组与捕获
用()可以将表达式分组,分组可以:
- 将多个元素作为一个整体,应用量词
- 捕获匹配的内容,供后续使用
// 分组应用量词
preg_match('/(ab)+/', 'ababab'); // 匹配ab重复1次或多次
// 捕获匹配内容
preg_match('/(\d{4})-(\d{2})-(\d{2})/', '2015-05-26', $m);
// $m[0] = '2015-05-26'(整个匹配)
// $m[1] = '2015'(第一个捕获组)
// $m[2] = '05'(第二个捕获组)
// $m[3] = '26'(第三个捕获组)非捕获分组
用(?:)可以创建非捕获分组,只分组不捕获:
preg_match('/(?:ab)+/', $str); // 分组但不捕获命名捕获组
用(?P<name>)可以给捕获组命名:
preg_match('/(?P<year>\d{4})-(?P<month>\d{2})/', $str, $m);
// $m['year'] = '2015'
// $m['month'] = '05'反向引用
用\1、\2等可以在正则中引用前面捕获的内容:
// 匹配重复的单词
preg_match('/\b(\w+)\s+\1\b/', 'hello hello world'); // 匹配hello hello断言
断言是一种零宽度匹配,只匹配位置,不消耗字符:
正向先行断言 (?=pattern) 匹配后面跟着pattern的位置:
preg_match('/\d+(?=元)/', '100元'); // 匹配后面跟着"元"的数字负向先行断言 (?!pattern) 匹配后面不跟着pattern的位置:
preg_match('/\d+(?!元)/', '100个'); // 匹配后面不跟着"元"的数字正向后行断言 (?<=pattern) 匹配前面是pattern的位置:
preg_match('/(?<=\$)\d+/', '$100'); // 匹配前面是$的数字负向后行断言 (?<!pattern) 匹配前面不是pattern的位置。
常用正则模式
邮箱验证
$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
preg_match($pattern, $email);手机号验证(中国大陆)
$pattern = '/^1[3-9]\d{9}$/';
preg_match($pattern, $phone);URL验证
$pattern = '/^https?:\/\/[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=]*$/';IP地址验证(IPv4)
$pattern = '/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/';身份证号验证(18位)
$pattern = '/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/';日期验证(YYYY-MM-DD)
$pattern = '/^\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])$/';中文字符匹配
$pattern = '/[\x{4e00}-\x{9fa5}]/u'; // 匹配单个中文字符HTML标签提取
// 提取所有a标签的href
preg_match_all('/<a[^>]+href="([^"]+)"[^>]*>(.*?)<\/a>/is', $html, $m);PHP正则函数实战
preg_match:验证表单
function validateEmail($email) {
return preg_match('/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $email);
}
function validatePhone($phone) {
return preg_match('/^1[3-9]\d{9}$/', $phone);
}pregmatchall:提取数据
// 从文本中提取所有邮箱
$text = '联系我:test@example.com 或 admin@site.org';
preg_match_all('/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/', $text, $matches);
print_r($matches[0]); // Array ( [0] => test@example.com [1] => admin@site.org )preg_replace:替换文本
// 将所有URL替换为链接
$text = '访问 https://example.com 了解更多';
$result = preg_replace('/https?:\/\/[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#]*/', '<a href="$0">$0</a>', $text);
// 去除HTML标签
$text = strip_tags($html); // 用strip_tags更简单
// 或用正则:preg_replace('/<[^>]+>/', '', $html);
// 敏感词替换
$text = preg_replace('/敏感词|脏话/', '***', $text);preg_split:分割字符串
// 按多种分隔符分割
$str = 'a,b,c;d e';
$parts = preg_split('/[,;\s]+/', $str);
// Array ( [0] => a [1] => b [2] => c [3] => d [4] => e )正则调试技巧
正则表达式写起来容易出错,调试很重要:
- 在线工具:用regex101.com、regexr.com等在线工具测试正则,实时查看匹配结果和解释
- 分步构建:不要一次性写复杂的正则,从简单开始,逐步添加,每步测试
- 注释:用x修饰符,可以在正则中加注释,提高可读性
- preglasterror():正则执行出错时,用preglasterror()查看错误原因
- 避免灾难性回溯:嵌套的量词可能导致灾难性回溯(ReDoS),正则执行超时甚至崩溃。避免写
(a+)+这样的模式
注意事项
- 正则不是万能的:处理HTML/XML用专门的解析器(DOMDocument、SimpleXML),不要用正则
- 性能考虑:复杂的正则可能很慢,大文本处理时注意性能
- UTF-8处理:处理中文等多字节字符时,加u修饰符
- 转义用户输入:用用户输入作为正则模式时,用preg_quote()转义特殊字符
- 安全:不要用/e修饰符(已废弃),避免代码注入
总结
正则表达式是处理字符串的强大工具,掌握它能大幅提升开发效率。
重点掌握:
- 基本语法:元字符、字符类、量词、分组
- 常用模式:邮箱、手机号、URL、IP等
- PHP函数:pregmatch、pregmatchall、pregreplace、preg_split
- 调试技巧:在线工具、分步构建、避免灾难性回溯
正则表达式需要多练多用,写得多了自然就熟练了。希望这篇指南能帮你掌握正则这个利器。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录