本文内容, 整理自网文Finer points of PHP regular expressions. 其分析过程剥茧抽丝, 丝丝入扣, 值得一读. 该文系统地列出了PHP中正则表达式常见特性, 我只摘取其中递归部分翻译整理出来.
之前一篇文章翻译了Perl语言中的递归正则表达式. 其实不少语言中的正则都是支持递归的, 例如本文要介绍的PHP正则递归. 虽然, 工作中最常用的正则表达式都很”正则”, 只用最基本的语法就能解决85%以上的问题, 而且合理有效地使用普通正则来解决复杂问题也是一门技巧与学问; 但是高级一点的语法的确有它存的价值, 有时不用它还真办不了事儿; 况且学习正则的乐趣也在于尝试各种各样的可能性, 满足自己无穷无尽的好奇心.
本文内容, 整理自网文Finer points of PHP regular expressions. 其分析过程剥茧抽丝, 丝丝入扣, 值得一读. 该文系统地列出了PHP中正则表达式常见特性, 我只摘取其中递归部分翻译整理出来.
正文
例子
什么时候会用到递归正则表达式呢? 当然是待匹配的字串中递归地出现某种模式时(貌似废话). 最经典的例子, 就是递归正则处理嵌套括号的问题了. 例子如下.
假设你的文本中包含了正确配对的嵌套括号. 括号的深度可以是无限层. 你想捕获这样的括号组.
恕我剧透, 标准答案是这样的:
2 |
$string = "some text (a(b(c)d)e) more text" ;
|
3 |
if (preg_match( "/\(([^()]+|(?R))*\)/" , $string , $matches ))
|
5 |
echo "<pre>" ; print_r( $matches ); echo "</pre>" ;
|
其输出结果是:
可见, 我们所需要的文本, 已经捕获到$matches[0]中了.
原理
现在思考原理.
上面的正则表达式中的关键点是(?R). (?R)的作用就是递归地替换它所在的整条正则表达式. 在每次迭代时, PHP 语法分析器都会将(?R)替换为”\(([^()]+|(?R))*\)“.
因此, 具体到上述的例子, 其正则表达式等价于:
"/\(([^()]+|\(([^()]+|\(([^()]+)*\))*\))*\)/"
但是上面的代码只适合深度为3层的括号. 对于未知深度的括号嵌套, 就只好使用这种正则了:
"/\(([^()]+|(?R))*\)/"
它不但能够匹配无限深度, 还简化了正则表达式的语法. 功能强大, 语法简洁.
现在来细看一下"/\(([^()]+|(?R))*\)/"是怎样匹配"(a(b(c)d)e)"的:
"(c)"这部分被正则式 "\(([^()]+)*\)" 匹配. 请注意, (c) 其实就相当于整个递归的一个缩影, 麻雀虽小五脏俱全, 因此它用到了整个正则表达式.
换言之, 下一步中的(c), 可以使用(?R) 来匹配.
(b(c)d)的匹配过程为:
"\("匹配"(";
"[^()]+"匹配"b";
(?R)匹配"(c)";
"[^()]+"匹配"d";
"\)"匹配")".
根据上面的匹配原理, 不难理解为什么数组的第2个元素$matches[1]与'e'等价. 子串'e'是在最后一次匹配迭代中被捕获. 匹配过程中, 只有最后一次的捕获结果才会保存到数组中.
rex注: 关于这个特性, 可以自行尝试一下, 看看使用正则式([a-z]+[0-9]+)+来匹配字串abc123xyz890, 其捕获结果$1是什么. 注意, 其结果与 Left Longest 原理并不冲突.
如果我们只需要捕获 $matches[0], 可以这样做:
2 |
$string = "some text (a(b(c)d)e) more text" ;
|
3 |
if (preg_match( "/\((?:[^()]+|(?R))*\)/" , $string , $matches ))
|
5 |
echo "<pre>" ; print_r( $matches ); echo "</pre>" ;
|
产生的结果相同:
所做的改动是捕获括号()改为非捕获捕获括号(?:)了.
还可以进一步完善为:
2 |
$string = "some text (a(b(c)d)e) more text" ;
|
3 |
if (preg_match( "/\((?>[^()]+|(?R))*\)/" , $string , $matches ))
|
5 |
echo "<pre>" ; print_r( $matches ); echo "</pre>" ;
|
这里我们用到了所谓的一次性模式(rex注: 余晟先生译的《精通正则表达式v3.0》中, 谓之”固化分组”. 可参考该书.) PHP手册也推荐只要条件允许, 就尽可能使用这种模式, 以便提升正则表达式的速度.
一次性模式很简单, 这里不再详述. 如果感兴趣, 可以参考PHP 官方手册. 如果您想深入学习PERL兼容式正则表达式, 请参考文末链接.
分享到:
相关推荐
这样,该表达式就能够匹配任意层数的嵌套括号了。 ##### 1.4 HTML标签的递归匹配 类似的,对于HTML标签的递归匹配,也可以构建类似的表达式。例如,对于`<font>`标签的匹配: ``` ((?!).|(((?!).)*))* ``` 这里...
假设你的文本中包含了正确配对的嵌套括号. 括号的深度可以是无限层. 你想捕获这样的括号组. 代码如下: <?php $string = “some text (a(b(c)d)e) more text”; if(preg_match(“/\(([^()]+|(?R))*\)/”,$string,...
编辑器会实时检测并高亮显示匹配的括号,当光标移动到一个括号上时,它的配对括号也会被突出显示,这对于编写复杂的嵌套结构非常有用。 3. **函数列表**:函数列表功能提供了快速浏览和跳转到代码中定义的函数的...
值得注意的是,虽然这个正则表达式能够很好地匹配大多数函数体,但在某些特殊情况下的函数可能会出现匹配失败的情况,比如包含复杂嵌套结构的函数体。因此,在实际应用中,可能还需要根据具体情况进行调整和优化。
匹配嵌套括号内的文本... 475 不能回溯到递归调用之内... 477 匹配一组嵌套的括号... 478 PHP效率... 478 模式修饰符S:“研究”. 478 扩展示例... 480 用PHP解析CSV.. 480 检查tagged data的嵌套正确性... ...
举例来说,假设我们需要从文本中提取正确配对的嵌套括号,比如`"some text (a(b(c)d)e) more text"`。使用递归正则表达式`\(([^()]+|(?R))*\)`,可以有效地捕获这样的括号组。在这个正则表达式中,`\(([^()]+|(?R))*...
数组的结构通常是 `$matches[0]` 存放所有完整匹配的字符串,`$matches[1]` 存放第一个括号中的子模式匹配的字符串,依此类推。 4. **flags**(可选):可以设置一些标志来改变匹配行为。例如,`PREG_PATTERN_ORDER...
需要注意的是,当使用方括号`[]`时,它定义了一个字符类,其中的`.`会被解释为匹配点字符(ASCII码为46),而不是匹配任何字符。如果要匹配点字符,应写成`\.`。因此,使用`/[.]*?<\/tr>/is`实际上是在匹配包含点...
- **单行或多行注释问题**:避免在注释中嵌套其他注释。 ##### 8.2 命名规则 - 避免使用“魔术数字”,即直接在代码中使用未定义的数字。 ##### 8.3 缩进 - 保持一致的缩进风格,提高代码可读性。 #### 十、...
- PCRE支持正则表达式的递归,使得能够处理复杂嵌套结构,如嵌套的括号。 7. **命名捕获组** - 除了传统的数字捕获组外,PCRE还支持使用名字来标识捕获组,便于后续的引用和提取。 8. **错误处理与调试** - `...
**Ctrl + Shift + 方括号**:选择括号内的内容。适用于编辑嵌套结构的代码。 以上是 Sublime Text 中与 PHP 编辑相关的常用快捷键汇总。通过熟练掌握这些快捷键,可以极大提高代码编写效率,减少重复劳动。希望本文...
- 提供的XHTML代码片段中有两处错误:内部的`<ul>`标签没有正确关闭,以及嵌套的`<ul>`标签不应直接放在`<li>`标签内。 7. **数组操作**: - `array_reverse()` 函数可以反转数组元素的顺序,如题目所示。 - `...
正则表达式(Regular Expression,简称regex)是一种模式匹配工具,用于在文本中搜索、查找、替换符合特定规则的字符串。PCRE(Perl Compatible Regular Expressions)是正则表达式的一种实现,它提供了与Perl语言...
四种括号可以任意嵌套,里面的内容含义,取决于由内到外的括号链 同层没有优先级,从右到左进行 //如果设计一个互斥组,可以实现m选n,目前不支持,不偏向这个,也没有更多括号了…… //*3~3 示例: qq 结果:qq...
R))*\)`:匹配圆括号内的内容,`(?R)`允许模式递归匹配。 构造这个正则表达式是为了匹配类似PHP数组的结构,并通过`preg_match_all`函数提取所有匹配的部分,然后通过`getarray`函数递归处理每个匹配到的数组元素,...
3. 匹配嵌套结构:在处理嵌套的括号、标签等结构时,非贪婪模式能避免过度匹配。 六、注意事项与优化 虽然贪婪模式在很多情况下很实用,但过度贪婪可能导致不期望的结果。因此,在编写正则表达式时,理解贪婪与非...
4.3.5 嵌套函数调用 61 4.3.6 递归函数 61 4.4 总结 65 第5章 中场一:数据库连接 67 5.1 开端 67 5.2 创建连接 67 5.3 获取HTML表单信息 69 5.4 使用HTML表单信息 70 5.5 common.inc文件 72 5.6 总结 73 第6章 ...
- **嵌套模式匹配**:支持递归正则表达式,允许在正则表达式中嵌套使用正则表达式。 - **预查和后查**:提供了预查和后查操作符,允许在不消耗匹配字符的情况下进行前瞻和后顾匹配。 - **子模式**:使用括号()来定...
`,这是为了在遇到嵌套标签时能够匹配到第一个闭合的标签,避免跨标签匹配的问题。 为了实现去除HTML标签的功能,本例中提供了JavaScript代码示例。通过定义一个`matchReg`函数,并将待处理的字符串作为参数传递给...
这些构造使得正则表达式能够处理复杂的模式匹配任务,例如识别嵌套结构、匹配平衡括号等。 PCRE库的POSIX兼容包装函数使得它能够与那些遵循POSIX标准的系统和语言无缝集成。POSIX标准定义了一套简单的正则表达式...