54、PHP正则表达式Preg函数接口详解

PHP正则表达式Preg函数接口详解

1. 前瞻和模式修饰符基础
  • 前瞻限制 :后向引用(Lookbehind)仅限于匹配固定长度文本的子表达式,不过允许顶级的不同固定长度的可选分支。
  • x模式修饰符 :x模式修饰符(自由间距和注释)仅识别ASCII空格,不识别Unicode中的其他空格。
2. Preg函数接口概述

PHP的正则表达式引擎接口是纯过程式的,由以下六个主要函数提供:
| 函数 | 用途 |
| — | — |
| pregRmatch | 检查正则表达式是否能在字符串中匹配,并从字符串中提取数据 |
| pregRmatchRall | 从字符串中提取数据 |
| pregRreplace | 在字符串副本中替换匹配的文本 |
| pregRreplaceRcallback | 为字符串中每个正则表达式匹配项调用一个函数 |
| pregRsplit | 将字符串分割成子字符串数组 |
| pregRgrep | 从数组中筛选出匹配或不匹配正则表达式的元素 |
| pregRquote | 转义字符串中的正则表达式元字符 |

此外,还有一些辅助函数:
- regRmatch:pregRmatch的非参与括号感知版本。
- pregRregexRtoRpattern:从正则表达式字符串创建preg模式字符串。
- pregRpatternRerror:检查preg模式字符串的语法错误。
- pregRregexRerror:检查正则表达式字符串的语法错误。

这些函数的实际行为受提供的参数类型和数量、函数标志以及正则表达式使用的模式修饰符的影响。下面是一些示例,让我们感受一下正则表达式在PHP中的样子和处理方式:

// 检查HTML标签是否为<table>标签
if (pregRmatch('/^<table\b/i', $tag))
    print "tag is a table tag\n";
// 检查文本是否为整数
if (pregRmatch('/^-?\d+$/',$userRinput))
    print "user input is an integer\n";
// 从字符串中提取HTML标题
if (pregRmatch('{<title>(.+?)</title>}si', $html, $matches))
    print "page title: $matches[1]\n";
// 将字符串中的数字视为华氏温度值并替换为摄氏温度值
$metric = pregRreplace('/(-?\d+(?:\.\d+)?)/e', 
    'floor(($1-32)*5/9 + 0.5)', 
    $string);
// 从充满简单逗号分隔值的字符串创建值数组
$valuesRarray = pregRsplit('!\s*,\s*!', $commaRseparatedRvalues);
3. “模式”参数

任何preg函数的第一个参数都是模式参数,它是由一对定界符包裹的正则表达式,可能后面还跟着模式修饰符。例如,在 pregRmatch('/^<table\b/i', $tag) 中,模式参数是 '/^<table\b/i' ,它是由一对斜杠(定界符)包裹的正则表达式 <table\b ,后面跟着i(不区分大小写匹配)模式修饰符。

4. PHP单引号字符串

由于正则表达式倾向于包含反斜杠,在提供这些模式参数作为字符串字面量时,使用PHP的单引号字符串最为方便。PHP单引号字符串只有两个字符串元序列: \' \\ ,分别用于在字符串值中包含单引号和反斜杠。

例如,要匹配Windows磁盘的根路径,如 C:\ ,正则表达式是 ^[A-Z]:\\$ ,在单引号字符串字面量中表示为 '^[A-Z]:\\\\$'

以下是一些示例,展示了单引号字符串中反斜杠的处理:

print '/^.+\/';
// 输出: /^.+\/
print '/^.+\\/';
// 输出: /^.+\/
print '/^.+\\\/';
// 输出: /^.+\\/
print '/^.+\\\\/';
// 输出: /^.+\\/
5. 定界符

preg引擎要求在正则表达式周围使用定界符,因为设计者希望提供更像Perl的外观,特别是在模式修饰符方面。通常使用斜杠作为定界符,但也可以使用任何非字母数字、非空格的ASCII字符(除反斜杠外)。常见的定界符对有 / ! #

如果第一个定界符是“开放”标点符号,如 { ( < [ ,则关闭定界符变为相应的匹配关闭标点符号,如 } ) > ] 。使用这些“配对”定界符时,定界符可以嵌套,但为了清晰起见,建议使用简单的形式,如 /(\d+)/

定界符可以在模式参数字符串的正则表达式部分进行转义,例如 /<B>(.+?)<\/B>/i 是允许的,但使用不同的定界符可能会使代码更清晰,如 !<B>(.+?)</B>!i {<B>(.+?)</B>}i

6. 模式修饰符

模式修饰符可以放在关闭定界符之后,或者在某些情况下,放在正则表达式内部,以修改模式使用的某些方面。以下是所有允许的模式修饰符的总结:
| 修饰符 | 内联形式 | 描述 |
| — | — | — |
| i | (?i) | 匹配时忽略字母大小写 |
| m | (?m) | 增强行锚点匹配模式 |
| s | (?s) | 点匹配所有字符的匹配模式 |
| x | (?x) | 自由间距和注释正则表达式模式 |
| u | | 将正则表达式和目标字符串视为UTF - 8编码 |
| X | (?X) | 启用PCRE“额外功能” |
| e | | 将替换内容作为PHP代码执行(仅用于pregRreplace) |
| S | | 调用PCRE的“研究”优化尝试 |
| U | (?U) | 交换 + +? 等的贪婪性 |
| A | | 将整个匹配锚定到尝试的初始起始位置 |
| D | | $ 仅在字符串末尾匹配,不在字符串末尾换行符之前匹配(如果使用m模式修饰符则忽略) |

7. 模式修饰符的使用方式
  • 正则表达式内部 :模式修饰符嵌入正则表达式时,可以独立出现以打开或关闭某个功能,如 (?i) 打开不区分大小写匹配, (?-i) 关闭。它们的作用范围持续到包含它们的括号组结束,或者到正则表达式结束。也可以用作模式修改范围,如 (?i:...) 在该范围内打开不区分大小写匹配, (?-sm:...) 在该范围内关闭s和m模式。
  • 正则表达式外部 :修饰符可以在最终定界符之后以任意顺序组合,例如 si 可以同时启用不区分大小写和点匹配所有字符的模式。
8. PHP特定的模式修饰符
  • u修饰符 :告诉preg正则表达式引擎将正则表达式和主题字符串的字节视为UTF - 8编码。使用此修饰符不会改变字节,只是改变引擎对它们的处理方式。默认情况下,preg引擎将传递给它的数据视为当前8位区域设置。如果知道数据是UTF - 8编码的,就使用此修饰符;否则,不要使用。
  • X修饰符 :打开PCRE“额外功能”,当前的效果是当反斜杠用于非已知元序列的情况时生成错误。例如,默认情况下, \k 对PCRE没有特殊意义,会被视为 k ,但使用X修饰符会导致“未识别的字符跟随\”致命错误。
  • S修饰符 :调用PCRE的“研究”功能,对正则表达式进行预分析,在某些情况下可以显著加快匹配尝试。
  • 其他修饰符 :A、D和U修饰符比较深奥,很少使用。A修饰符将匹配锚定到匹配尝试开始的位置;D修饰符将每个 $ 转换为 \z ;U修饰符交换元字符的贪婪性。
9. “未知修饰符”错误

在编写程序时,有时会突然出现“未知修饰符”错误,这通常是因为在创建模式参数时忘记为正则表达式添加定界符。例如:

pregRmatch('<(\w+)([^>]+)>', $html);

这里,pregRmatch会将 < 视为打开定界符,导致后续代码被错误解释。正确的做法是添加定界符,如 pregRmatch('/<(\w+)(.+?)>/', $html);

10. pregRmatch函数详解
  • 用法 pregRmatch(pattern, subject [, matches [, flags [, offset ]]])
  • 参数总结
    • pattern :模式参数,即由定界符包裹的正则表达式,可选地带有修饰符。
    • subject :要搜索的目标字符串。
    • matches :可选变量,用于接收匹配数据。
    • flags :可选标志,影响函数整体行为,只允许一个标志 PREG_OFFSET_CAPTURE
    • offset :可选的从零开始的偏移量,指定在目标字符串中开始匹配尝试的位置。
  • 返回值 :如果找到匹配项,则返回真值;否则返回假值。
  • 简单示例
if (pregRmatch('/\.(jpe?g|png|gif|bmp)$/i', $url)) {
    // URL似乎是图像
}
if (pregRmatch('{^https?://}', $uri)) {
    // URI是http或https
}
if (pregRmatch('/\b MSIE \b/x', $_SERVER['HTTP_USER_AGENT'])) {
    // 浏览器是IE
}
  • 捕获匹配数据 :pregRmatch的第三个可选参数是一个变量,用于接收匹配结果的信息。成功匹配后, $matches 数组的设置如下:
    • $matches[0] 是正则表达式匹配的整个文本。
    • $matches[1] 是第一个捕获括号匹配的文本。
    • $matches[2] 是第二个捕获括号匹配的文本,依此类推。

例如:

// 从完整路径中分离文件名
if (pregRmatch('{ / ([^/]+) $}x', $WholePath, $matches))
    $FileName = $matches[1];

需要注意的是,只有在pregRmatch返回真值后,才能安全使用 $matches 变量。如果匹配不成功或出现错误(如错误的模式或函数标志),则返回假值。有些错误会将 $matches 清空为一个空数组,而有些错误会保留其之前的值,因此不能仅仅因为 $matches 不为空就认为它是有效的。

  • 更复杂的示例
// 从URL中提取协议、主机名和端口号
if (pregRmatch('{^(\w+):\/\/([^\/:]+)(?::(\d+))?}', $url, $matches)) {
    $protocol = $matches[1];
    $hostname = $matches[2];
    $port = isset($matches[3]) ? $matches[3] : ($protocol == 'http' ? 80 : 443);
    echo "Protocol: $protocol\n";
    echo "Hostname: $hostname\n";
    echo "Port: $port\n";
}
  • 尾随“非参与”元素的处理 :一组不参与最终匹配的括号在对应的 $matches 元素中会产生一个空字符串。需要注意的是,尾随的非参与捕获元素甚至不会包含在 $matches 中。例如,如果 (\d+) 参与了匹配, $matches[3] 会得到一个数字;如果它没有参与, $matches[3] 在数组中甚至不存在。
  • 命名捕获 :使用命名捕获可以使代码更具自文档性。例如:
if (pregRmatch('{^(?P<protocol>\w+):\/\/(?P<hostname>[^\/:]+)(?::(?P<port>\d+))?}', $url, $matches)) {
    $protocol = $matches['protocol'];
    $hostname = $matches['hostname'];
    $port = isset($matches['port']) ? $matches['port'] : ($protocol == 'http' ? 80 : 443);
    echo "Protocol: $protocol\n";
    echo "Hostname: $hostname\n";
    echo "Port: $port\n";
}

使用命名捕获时,编号捕获仍然会插入到 $matches 中,这会导致一定的重复,但这是为了方便和清晰而付出的代价。为了清晰起见,除了 $matches[0] 作为整体匹配外,不建议同时使用命名和数字引用 $matches 的元素。

11. pregRmatchAll函数
  • 用法 pregRmatchAll(pattern, subject, matches [, flags [, offset ]])
  • 参数总结
    • pattern :模式参数,同 pregRmatch ,是带定界符和可选修饰符的正则表达式。
    • subject :目标字符串,即要进行搜索的字符串。
    • matches :用于接收所有匹配结果的数组。
    • flags :可选标志,有多种取值,如 PREG_PATTERN_ORDER (默认,按模式中捕获组的顺序返回结果)、 PREG_SET_ORDER (按每个匹配项的顺序返回结果)等。
    • offset :可选的从零开始的偏移量,指定在目标字符串中开始匹配尝试的位置。
  • 返回值 :返回匹配的次数,如果发生错误则返回 false
  • 示例
$html = '<p>First paragraph</p><p>Second paragraph</p>';
if (pregRmatchAll('{<p>(.*?)</p>}i', $html, $matches, PREG_PATTERN_ORDER)) {
    foreach ($matches[1] as $paragraph) {
        echo $paragraph . "\n";
    }
}

在这个示例中, pregRmatchAll 会找出所有的 <p> 标签内的内容,并存储在 $matches 数组中。由于使用了 PREG_PATTERN_ORDER 标志, $matches[1] 包含了所有捕获组匹配的内容。

12. pregRreplace函数
  • 用法 pregRreplace(pattern, replacement, subject [, limit [, count ]])
  • 参数总结
    • pattern :模式参数,正则表达式。
    • replacement :替换字符串或数组,如果使用 e 修饰符,会将其作为PHP代码执行。
    • subject :目标字符串,要进行替换操作的字符串。
    • limit :可选参数,指定最大替换次数,默认值为 -1 表示无限制。
    • count :可选参数,用于接收实际发生替换的次数。
  • 返回值 :返回替换后的字符串,如果 subject 是数组,则返回替换后的数组。
  • 示例
$text = 'The quick brown fox jumps over the lazy dog.';
$newText = pregRreplace('/fox/', 'cat', $text);
echo $newText;

在这个示例中, pregRreplace 将字符串中的 fox 替换为 cat

如果使用 e 修饰符,可以执行PHP代码进行替换:

$string = 'The price is $10, $20, $30.';
$newString = pregRreplace('/\$(\d+)/e', '($1 * 1.1)', $string);
echo $newString;

这里将字符串中的价格增加了10%。

13. pregRreplaceCallback函数
  • 用法 pregRreplaceCallback(pattern, callback, subject [, limit [, count ]])
  • 参数总结
    • pattern :模式参数,正则表达式。
    • callback :回调函数,用于处理每个匹配项。
    • subject :目标字符串。
    • limit :可选参数,最大替换次数。
    • count :可选参数,接收实际替换次数。
  • 返回值 :返回替换后的字符串或数组。
  • 示例
$string = 'The price is $10, $20, $30.';
$newString = pregRreplaceCallback('/\$(\d+)/', function($matches) {
    return '$' . ($matches[1] * 1.1);
}, $string);
echo $newString;

在这个示例中,使用回调函数将每个匹配到的价格增加了10%。

14. pregRsplit函数
  • 用法 pregRsplit(pattern, subject [, limit [, flags ]])
  • 参数总结
    • pattern :模式参数,用于分割字符串的正则表达式。
    • subject :目标字符串。
    • limit :可选参数,指定分割后的最大元素数。
    • flags :可选标志,如 PREG_SPLIT_NO_EMPTY (忽略空元素)等。
  • 返回值 :返回分割后的数组。
  • 示例
$commaSeparated = 'apple,banana,orange';
$array = pregRsplit('/,\s*/', $commaSeparated);
print_r($array);

这个示例将逗号分隔的字符串分割成数组。

15. pregRgrep函数
  • 用法 pregRgrep(pattern, input [, invert ])
  • 参数总结
    • pattern :模式参数,正则表达式。
    • input :输入数组,要进行筛选的数组。
    • invert :可选参数,布尔值,如果为 true ,则返回不匹配的元素。
  • 返回值 :返回匹配或不匹配的元素组成的数组。
  • 示例
$array = ['apple', 'banana', 'cherry'];
$matched = pregRgrep('/^a/', $array);
print_r($matched);

这个示例返回以字母 a 开头的元素组成的数组。

16. pregRquote函数
  • 用法 pregRquote(str [, delimiter ])
  • 参数总结
    • str :要进行转义的字符串。
    • delimiter :可选参数,指定定界符,会对其进行转义。
  • 返回值 :返回转义后的字符串。
  • 示例
$string = 'Hello. World!';
$escaped = pregRquote($string, '/');
echo $escaped;

这个示例将字符串中的正则表达式元字符进行了转义。

17. 正则表达式使用流程总结

下面是一个使用PHP正则表达式的基本流程图:

graph TD;
    A[定义正则表达式模式] --> B[选择合适的preg函数];
    B --> C[设置函数参数];
    C --> D[执行函数];
    D --> E{是否匹配成功};
    E -- 是 --> F[处理匹配结果];
    E -- 否 --> G[处理未匹配情况];
18. 注意事项和最佳实践
  • 定界符和模式修饰符 :使用合适的定界符和模式修饰符,避免“未知修饰符”错误。在编写正则表达式时,要仔细检查定界符是否正确添加。
  • 单引号字符串 :优先使用单引号字符串来表示正则表达式,减少反斜杠转义的复杂性。
  • 错误处理 :在使用正则表达式函数时,要考虑错误情况,如无效的模式或函数标志。可以通过检查函数的返回值来判断是否发生错误。
  • 性能优化 :对于复杂的正则表达式,可以使用 S 模式修饰符调用PCRE的“研究”功能进行预分析,提高匹配效率。

通过以上对PHP正则表达式Preg函数接口的详细介绍,我们可以看到这些函数提供了强大而灵活的字符串处理能力。无论是简单的匹配、替换,还是复杂的字符串分割和筛选,都可以通过合理使用这些函数和正则表达式来实现。在实际开发中,要根据具体需求选择合适的函数和模式修饰符,并注意错误处理和性能优化,以确保代码的正确性和高效性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值