32、PHP 正则表达式全解析

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 正则表达式的使用,为你的项目开发带来更多便利。在实际应用中,要根据具体情况灵活运用各种技巧和方法,同时注意性能和错误处理,以确保程序的稳定性和可靠性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值