PHP 正则表达式全解析
1. 正则表达式基础
正则表达式是一种强大的文本匹配工具。例如,正则表达式
.*
可以匹配任意字符,而
^.*3
则会匹配从行首到行中最后一个数字 “3” 的所有字符。
下面通过日期匹配的例子来深入了解正则表达式。
1.1 日期格式匹配
-
格式一:”Saturday, April 30, 2011”
匹配该日期格式的正则表达式为:/[A-Z][a-z]{2,},\s[A-Z][a-z]{2,}\s\d{1,2},\s\d{4}/。其含义为:一个大写字母,后面至少跟两个小写字母和一个逗号,接着是一个空格、一个大写字母、至少两个小写字母、一个空格、1 或 2 位数字、一个逗号、一个空格,最后是精确的四位年份数字。
以下是一个 PHP 代码示例来测试该正则表达式:
<?php
$expr = '/[A-Z][a-z]{2,},\s[A-Z][a-z]{2,}\s\d{1,2},\s\d{4}/';
$item = 'Saturday, April 30, 2011.';
if (preg_match($expr, $item)) {
print "Matches\n";
} else {
print "Doesn't match.\n";
}
?>
如果想让正则表达式精确匹配到行尾,可以使用
$
符号,如
/[A-Z][a-z]{2,},\s[A-Z][a-z]{2,}\s\d{1,2},\s\d{4}$/
;若要匹配到行首,则使用
^
符号,匹配整行的正则表达式为
/^.*$/
。
-
格式二:”YYYY-MM-DD”
若要解析这种日期格式并提取年、月、日,可以使用分组匹配。正则表达式为:/(\d{4})-(\d{2})-(\d{2})/。
以下是使用
preg_match
函数实现的代码:
<?php
$expr = '/(\d{4})-(\d{2})-(\d{2})/';
$item = 'Event date: 2011-05-01';
$matches=array();
if (preg_match($expr, $item,$matches)) {
foreach(range(0,count($matches)-1) as $i) {
printf("%d:-->%s\n",$i,$matches[$i]);
}
list($year,$month,$day)=array_splice($matches,1,3);
print "Year:$year Month:$month Day:$day\n";
} else {
print "Doesn't match.\n";
}
?>
执行该代码后,输出结果如下:
0:-->2011-05-01
1:-->2011
2:-->05
3:-->01
Year:2011 Month:05 Day:01
其中,
$matches
数组的第 0 个元素是匹配整个表达式的字符串,后续元素依次是各个分组匹配的结果。
1.2 URL 解析
URL 的一般形式为
http://hostname:port/loc?arg=value
,解析这种 URL 的正则表达式为:
/^https?:\/\/[^:\/]+:?\d*\/[^?]*.*/
。
下面是该正则表达式的详细分析:
| 部分 | 含义 |
| ---- | ---- |
|
^
| 匹配字符串的开始 |
|
https?
| 匹配 “http” 或 “https” |
|
:\/\/
| 匹配 “://” |
|
[^:\/]+
| 匹配一个或多个非冒号和非斜杠的字符 |
|
:?
| 匹配零个或一个冒号 |
|
\d*
| 匹配零个或多个数字 |
|
\/
| 匹配斜杠 |
|
[^?]*
| 匹配零个或多个非问号的字符 |
|
.*
| 匹配任意字符 |
PHP 对正则表达式的分隔符非常宽松,可以使用其他分隔符,如方括号或竖线。例如,
[^https?://[^:/]+:?\d*/[^?]*.*]
或
|^https?://[^:/]:?\d*/[^?]*.*|
都是有效的。
以下是一个测试 URL 匹配的 PHP 代码:
<?php
$expr = '[^https*://[^:/]+:?\d*/[^?]*.*]';
$item = 'https://myaccount.nytimes.com/auth/login?URI=http://';
if (preg_match($expr, $item)) {
print "Matches\n";
} else {
print "Doesn't match.\n";
}
?>
若要提取 URL 中的主机名、端口、目录和参数,可以使用分组匹配,正则表达式为:
[^https*://([^:/]+):?(\d*)/([^?]*)\??(.*)]
。
以下是提取 URL 各部分的 PHP 代码:
<?php
$expr = '[^https*://([^:/]+):?(\d*)/([^?]*)\??(.*)]';
$item = 'https://myaccount.nytimes.com/auth/login?URI=http://';
$matches = array();
if (preg_match($expr, $item, $matches)) {
list($host, $port, $dir, $args) = array_splice($matches, 1, 4);
print "Host=>$host\n";
print "Port=>$port\n";
print "Dir=>$dir\n";
print "Arguments=>$args\n";
} else {
print "Doesn't match.\n";
}
?>
执行该代码后,输出结果如下:
Host=>myaccount.nytimes.com
Port=>
Dir=>auth/login
Arguments=>URI=http://
2. 正则表达式的内部选项
正则表达式有一些内部选项可以改变其匹配行为。
2.1 忽略大小写选项 “i”
若要忽略大小写进行匹配,可以使用
(?i)
选项。例如,正则表达式
[(?i)^https?://([^:/]+):?(\d*)/([^?]*)\??(.*)]
可以匹配大小写混合的 URL。
以下是一个简单的示例:
<?php
$expr = '/Mladen (?i)g/';
$str1 = 'Mladen G';
$str2 = 'Mladen g';
$str3 = 'MLADEN G';
if (preg_match($expr, $str1)) {
echo "Match: $str1\n";
}
if (preg_match($expr, $str2)) {
echo "Match: $str2\n";
}
if (preg_match($expr, $str3)) {
echo "Match: $str3\n";
}
?>
该代码会匹配
Mladen G
和
Mladen g
,但不匹配
MLADEN G
。
2.2 多行选项 “m”
默认情况下,正则表达式解析遇到换行符
\n
就会停止。使用
(?m)
选项可以改变这种行为,使解析直到输入结束才停止。
2.3 “D” 选项
“D” 选项表示元字符 “$” 只匹配输入的结束,而不匹配字符串内的换行符。
这些选项可以组合使用,如
(?imD)
可以同时设置忽略大小写、多行匹配和 “$” 只匹配输入结束的选项。
也可以在正则表达式的最后使用传统的全局修饰符,如
[^https?://([^:/]+):?(\d*)/([^?]*)\??(.*)]i
。新的选项表示法可以在表达式的任何位置指定,只影响修饰符后面的部分;而在最后指定修饰符则会影响整个表达式。
3. 正则表达式的贪婪性
正则表达式默认是贪婪的,即会尽可能多地匹配输入字符串。例如,正则表达式
<img.*>
会匹配从
<img
开始到最后一个 “>” 的所有字符。
以下是一个示例代码:
<?php
$expr = '/<img.*>/';
$item = '<a><img src="file">text</a>"';
$matches=array();
if (preg_match($expr, $item,$matches)) {
printf( "Match:%s\n",$matches[0]);
} else {
print "Doesn't match.\n";
}
?>
执行该代码后,输出结果为:
Match:<img src="file">text</a>
这是因为 “.*>” 匹配了尽可能多的字符,直到遇到最后一个 “>”,而这个 “>” 是
</a>
标签的一部分。
使用问号可以使 “*” 和 “+” 量词变为非贪婪模式,即匹配最少数量的字符。将正则表达式修改为
<img.*?>
后,匹配会在遇到第一个 “>” 字符时停止,得到期望的结果:
<?php
$expr = '/<img.*?>/';
$item = '<a><img src="file">text</a>"';
$matches=array();
if (preg_match($expr, $item,$matches)) {
printf( "Match:%s\n",$matches[0]);
} else {
print "Doesn't match.\n";
}
?>
执行该代码后,输出结果为:
Match:<img src="file">
在解析 HTML 或 XML 时,非贪婪修饰符非常有用,因为需要精确匹配标签的边界。
4. PHP 正则表达式函数
除了
preg_match
函数,PHP 还有其他正则表达式函数,如
preg_replace
、
preg_split
和
preg_grep
。
4.1 字符串替换:
preg_replace
preg_replace
函数的语法为:
$result = preg_replace($pattern,$replacement,$input,$limit,$count);
| 参数 | 含义 |
|---|---|
$pattern
| 要匹配的正则表达式 |
$replacement
| 用于替换的字符串或数组 |
$input
| 输入的字符串或数组 |
$limit
| 替换的最大次数,-1 表示无限制(默认值) |
$count
| 可选参数,用于存储实际替换的次数 |
以下是一个使用
preg_replace
函数的示例:
<?php
$cookie = <<<'EOT'
Now what starts with the letter C?
Cookie starts with C
Let's think of other things that starts with C
Uh ahh who cares about the other things
C is for cookie that's good enough for me
C is for cookie that's good enough for me
C is for cookie that's good enough for me
Ohh cookie cookie cookie starts with C
EOT;
$expression = array("/(?i)cookie/", "/C/");
$replacement = array("donut", "D");
$donut = preg_replace($expression, $replacement, $cookie);
print "$donut\n";
?>
该代码会将字符串中的 “cookie” 替换为 “donut”,将 “C” 替换为 “D”。
另一个示例是生成 SQL 的
truncate table
命令:
<?php
$tables = array("emp", "dept", "bonus", "salgrade");
foreach ($tables as $t) {
$trunc = preg_replace("/^(\w+)/", "truncate table $1;", $t);
print "$trunc\n";
}
?>
执行该代码后,输出结果如下:
truncate table emp;
truncate table dept;
truncate table bonus;
truncate table salgrade;
4.2 字符串分割:
preg_split
preg_split
函数是
explode
函数的强大扩展。
explode
函数只能根据固定的分隔符分割字符串,而
preg_split
可以使用正则表达式作为分隔符。
例如,要分割字符串
'A, B,C .D'
,可以使用以下正则表达式:
\s*[,.]\s*
。
以下是一个使用
preg_split
函数的示例:
<?php
$a = 'A, B,C .D';
$result = preg_split('/\s*[,.]\s*/', $a);
print_r($result);
?>
该代码会将字符串分割为数组
['A', 'B', 'C', 'D']
。
4.3 数组过滤:
preg_grep
preg_grep
函数类似于命令行工具
grep
,用于过滤数组中的元素。其语法为:
$results = preg_grep($pattern, $input);
以下是一个使用
preg_grep
函数的示例:
<?php
$input = glob('/usr/share/pear/*');
$pattern = '/\.php$/';
$results = preg_grep($pattern, $input);
printf("Total files:%d PHP files:%d\n", count($input), count($results));
foreach ($results as $key => $val) {
printf("%d ==> %s\n", $key, $val);
}
?>
该代码会过滤出
/usr/share/pear/
目录下所有以
.php
结尾的文件。
综上所述,正则表达式在 PHP 中是一种非常强大的工具,可以用于文本匹配、字符串替换、分割和过滤等多种操作。但在使用时需要谨慎,因为不正确的正则表达式可能会导致意外的结果。
下面是一个简单的流程图,展示了正则表达式的使用流程:
graph TD;
A[定义正则表达式] --> B[选择匹配函数];
B --> C{是否匹配成功};
C -- 是 --> D[处理匹配结果];
C -- 否 --> E[不做处理或给出提示];
通过以上的介绍和示例,相信你对 PHP 正则表达式有了更深入的理解和掌握。在实际应用中,可以根据具体需求灵活运用这些知识。
4. 正则表达式函数的深入应用与注意事项
4.4 正则表达式函数的性能考量
在使用正则表达式函数时,性能是一个需要重点关注的问题。正则表达式的处理通常比简单的字符串比较更复杂,因此会带来额外的开销。
例如,在可以使用
explode
函数完成字符串分割的情况下,就不建议使用
preg_split
。因为
explode
函数只进行简单的字符串比较,而
preg_split
需要进行正则表达式的解析和匹配,性能上会有明显差异。
以下是一个对比示例:
<?php
// 使用 explode 函数分割字符串
$a = 'A,B,C,D';
$start1 = microtime(true);
$explodeResult = explode(',', $a);
$end1 = microtime(true);
$explodeTime = $end1 - $start1;
// 使用 preg_split 函数分割字符串
$start2 = microtime(true);
$pregSplitResult = preg_split('/,/', $a);
$end2 = microtime(true);
$pregSplitTime = $end2 - $start2;
echo "explode 函数用时: ". $explodeTime. " 秒\n";
echo "preg_split 函数用时: ". $pregSplitTime. " 秒\n";
?>
运行该代码可以发现,
explode
函数的执行时间通常会比
preg_split
函数短。
4.5 正则表达式的错误处理
在使用正则表达式函数时,可能会遇到各种错误。例如,正则表达式语法错误、内存不足等。PHP 提供了一些方法来处理这些错误。
可以使用
preg_last_error
函数来获取最后一次正则表达式操作的错误代码。以下是一个示例:
<?php
$expr = '/[a-z'; // 错误的正则表达式
$item = 'test';
if (!preg_match($expr, $item)) {
$errorCode = preg_last_error();
switch ($errorCode) {
case PREG_NO_ERROR:
echo "No error\n";
break;
case PREG_INTERNAL_ERROR:
echo "Internal error\n";
break;
case PREG_BACKTRACK_LIMIT_ERROR:
echo "Backtrack limit error\n";
break;
case PREG_RECURSION_LIMIT_ERROR:
echo "Recursion limit error\n";
break;
case PREG_BAD_UTF8_ERROR:
echo "Bad UTF-8 error\n";
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
echo "Bad UTF-8 offset error\n";
break;
default:
echo "Unknown error\n";
}
}
?>
通过这种方式,可以根据不同的错误代码进行相应的处理,提高程序的健壮性。
5. 正则表达式在实际项目中的应用场景
5.1 表单验证
在 Web 开发中,表单验证是一个常见的需求。可以使用正则表达式来验证用户输入的内容是否符合要求。
以下是一些常见的表单验证示例:
- 邮箱验证 :
<?php
$email = 'test@example.com';
$pattern = '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/';
if (preg_match($pattern, $email)) {
echo "Valid email address\n";
} else {
echo "Invalid email address\n";
}
?>
- 手机号码验证 :
<?php
$phone = '13800138000';
$pattern = '/^1[3-9]\d{9}$/';
if (preg_match($pattern, $phone)) {
echo "Valid phone number\n";
} else {
echo "Invalid phone number\n";
}
?>
5.2 数据清洗
在处理大量数据时,可能会遇到一些不规范的数据。可以使用正则表达式来清洗这些数据,去除不必要的字符。
例如,去除字符串中的 HTML 标签:
<?php
$html = '<p>Hello, <b>World!</b></p>';
$pattern = '/<[^>]+>/';
$cleaned = preg_replace($pattern, '', $html);
echo $cleaned;
?>
6. 正则表达式的高级技巧
6.1 回溯引用
回溯引用是正则表达式中的一个高级特性,它允许在正则表达式中引用之前匹配的内容。
例如,匹配重复的单词:
<?php
$text = 'The the dog';
$pattern = '/\b(\w+)\s+\1\b/i';
if (preg_match($pattern, $text)) {
echo "Found repeated word\n";
} else {
echo "No repeated word found\n";
}
?>
在这个例子中,
\1
表示引用第一个捕获组匹配的内容。
6.2 前瞻和后顾断言
前瞻和后顾断言用于在匹配时检查某个位置之前或之后的内容,但不消耗字符。
- 正向前瞻断言 :
<?php
$text = 'apple123';
$pattern = '/\w+(?=\d+)/';
if (preg_match($pattern, $text, $matches)) {
echo $matches[0];
}
?>
这个正则表达式会匹配后面跟着数字的单词。
- 负向后顾断言 :
<?php
$text = '123apple';
$pattern = '/(?<!\d+)\w+/';
if (preg_match($pattern, $text, $matches)) {
echo $matches[0];
}
?>
这个正则表达式会匹配前面不是数字的单词。
7. 总结
正则表达式在 PHP 中是一个功能强大且灵活的工具,可用于多种场景,如文本匹配、字符串替换、分割和过滤等。以下是对本文内容的总结:
| 功能 | 相关函数 | 示例代码 |
|---|---|---|
| 文本匹配 |
preg_match
|
preg_match('/pattern/', $string);
|
| 字符串替换 |
preg_replace
|
preg_replace('/pattern/', 'replacement', $string);
|
| 字符串分割 |
preg_split
|
preg_split('/pattern/', $string);
|
| 数组过滤 |
preg_grep
|
preg_grep('/pattern/', $array);
|
在使用正则表达式时,需要注意以下几点:
1. 性能问题:避免在简单场景下使用复杂的正则表达式,优先使用内置函数。
2. 错误处理:使用
preg_last_error
函数处理可能出现的错误。
3. 正则表达式的复杂度:过于复杂的正则表达式可能会导致难以维护和调试。
下面是一个更详细的流程图,展示了使用正则表达式进行文本处理的完整流程:
graph TD;
A[明确需求] --> B[设计正则表达式];
B --> C[选择合适的函数];
C --> D[执行正则操作];
D --> E{操作是否成功};
E -- 是 --> F[处理结果];
E -- 否 --> G[检查错误代码];
G --> H{是否为语法错误};
H -- 是 --> B;
H -- 否 --> I[进行错误处理];
F --> J[输出结果];
I --> B;
通过深入学习和实践,你可以熟练掌握 PHP 正则表达式的使用,为你的项目开发带来更多便利。在实际应用中,要根据具体情况灵活运用各种技巧和方法,同时注意性能和错误处理,以确保程序的稳定性和可靠性。
超级会员免费看
980

被折叠的 条评论
为什么被折叠?



