子例程——迹忆客-ag捕鱼王app官网
正则表达式子例程
perl 5.10,pcre 4.0和ruby 1.9支持正则表达式子例程调用。子例程与正则表达式递归非常相似。子例程调用仅匹配捕获组内的正则表达式,而不是再次匹配整个正则表达式。您可以从正则表达式中的任何地方对任何捕获组进行子例程调用。如果将调用放在要调用的组内,则将有一个递归捕获组。
与正则表达式递归一样,可以使用多种语法来实现完全相同的功能。perl使用(?1)
调用编号组,(? 1)
调用下一个组,(?-1)
调用前一个组,以及(?&name)
调用命名组。我们可以使用所有这些引用相同的组。(? 1)(?'name'[abc])(?1)(?-1))?&name)
匹配长度为五个字母且仅由字母表的前三个字母组成的字符串。此正则表达式与[abc](?’name’[abc])[abc][abc][abc]
效果是相同的。
pcre是第一个支持子例程调用的正则表达式引擎。(?p
与(?p
一样匹配三个字母。(?1)
是对编号组的调用,而(?p>name)
是对命名组的调用。后者在pcre手册页中称为“ python语法”。尽管此语法模仿了python用于命名捕获组的语法,但这是pcre的发明。python不支持子例程调用或递归。pcre 7.2为相对调用添加了(? 1)和(?-1)。pcre 7.7添加了perl 5.10和ruby 2.0使用的所有语法。最新版本的php,delphi和r也支持所有这些语法,因为它们的regex函数都是基于pcre的。
ruby 1.9和更高版本使用的语法看起来更像是反向引用。\g<1>
和\g’1’
调用编号组,\g
和\g’name’
调用命名组,而\g<-1>
和\g’-1’
调用前一组。ruby 2.0添加\g< 1>
和\g’ 1’
来调用下一个组。\g< 1>(?
和\g’ 1’(?’name’[abc])\g’1’\g’-1’\g’name’
与ruby 2.0中的perl示例匹配相同的5个字母字符串。带尖括号和引号的语法可以互换使用。
jgsoft v2支持所有三组语法。稍后我们将看到,perl,pcre和ruby在子例程调用期间,对于capture,backreferences和backtracking的处理是有所不同的。当他们复制彼此的语法时,他们没有复制彼此的行为。但是,jgsoft v2复制了它们的语法和行为。因此,jgsoft v2具有三种执行正则表达式递归的方法,我们可以使用不同的语法来选择它们。但是,这些差异不会在此页面的基本示例中展现出来。
boost 1.42从perl复制了语法,但其实现的过程中是有bug的,而bug在1.62版本中仍未全部修复。最重要的是,对于不是*
或{0,}的量词会导致子例程调用行为异常。这在boost 1.60中已部分修复,可以正确处理?和{0,1}。
boost不支持用于子例程调用的ruby语法。在boost中\g<1>
是捕获组1的反向引用(而不是子例程调用)。因此([ab])\g<1>
可以匹配aa和bb,但不能匹配ab或ba 。在ruby中,相同的正则表达式将匹配aa,bb,ab和ba。其他的正则表达式引擎都不会将此语法用于反向引用。
匹配平衡结构
与整个正则表达式的递归相比,递归到捕获组是匹配平衡结构的一种更灵活的方式。我们可以将正则表达式封装在捕获组中,递归捕获组,而不是整个正则表达式中,然后在捕获组外添加锚点。\a(b(?:m|(?1))*e)\z
是通用正则表达式,用于检查字符串是否完全由平衡的结构组成。同样,b是结构开始的地方,m是可能在结构的中间发生的事情,e是可能在结构的末尾出现的事情。为了获得正确的结果,b ,m和e中的任何两个都不能匹配相同的文本。我们可以使用原子组而不是非捕获组来提高性能:\a(b(?>m|(?1))*e)\z
。
类似地,\ao*(b(?:m|(?1))*eo*) \z
和优化后的正则表达式\ao* (b(?>m|(?1))* eo* ) \z
匹配仅由一个或多个正确平衡的结构序列组成的字符串,中间可能还有其他文本。在这里,o是在平衡构造之外可能发生的事情。它通常与m相同。o应该不能与b或e匹配相同的文本。
\a(\((?>[^()]|(?1))*\))\z
匹配一个仅由一对完全平衡的括号组成的一个字符串,括号之间可能带有文本。\a[^()]* (\((?>[^()]|(?1))* \)[^()]* ) \z
。
多次匹配相同的结构
在使用子例程调用时,需要多次在正则表达式的不同部分中匹配相同类型的结构(但不是完全相同的文本)的正则表达式可以更短,更简洁。假设我们需要一个正则表达式来匹配这样的患者记录:
name: john doe
born: 17-jan-1964
admitted: 30-jul-2013
released: 3-aug-2013
进一步假设我们需要相当准确地匹配日期格式,以便正则表达式可以过滤掉无效记录,从而筛选出有效记录留给人们去检查。在大多数regex中,可以使用free-spacing语法轻松地通过下面的正则表达式进行此操作:
^name:\ (.*)\r?\n
born:\ (?:3[01]|[12][0-9]|[1-9])
-(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
-(?:19|20)[0-9][0-9]\r?\n
admitted:\ (?:3[01]|[12][0-9]|[1-9])
-(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
-(?:19|20)[0-9][0-9]\r?\n
released:\ (?:3[01]|[12][0-9]|[1-9])
-(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
-(?:19|20)[0-9][0-9]$
通过子例程调用,我们可以使此正则表达式更短,更易于阅读且更易于维护:
^name:\ (.*)\r?\n
born:\ (?'date'(?:3[01]|[12][0-9]|[1-9])
-(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
-(?:19|20)[0-9][0-9])\r?\n
admitted:\ \g'date'\r?\n
released:\ \g'date'$
```
### 单独的子例程定义
***
在perl,pcre和jgsoft v2,我们可以通过下面的语法更进一步使用特殊的define组:`(?(define)(?'subroutine'regex))`。尽管这看起来像是一个条件组,它引用了不存在的组define,该组包含单个命名组“ subroutine”,但define组是一种特殊的语法。固定文本`(?(define)` 打开该组。右括号关闭该组。此特殊组告诉正则表达式引擎忽略其内容,而不是解析命名和编号的捕获组。我们可以在其中放入尽可能多的捕获组定义组只要我们喜欢。定义组本身永远不匹配任何东西,从来不匹配。它是完全忽略的。正则表达式`foo(?(define)(?'subroutine'skipped))bar`匹配foobar。定义组在此正则表达式中完全多余,因为其中没有对内部任何组的调用。
使用define组,我们的正则表达式变为:
```bash
(?(define)(?'date'(?:3[01]|[12][0-9]|[1-9])
-(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
-(?:19|20)[0-9][0-9]))
^name:\ (.*)\r?\n
born:\ (?p>date)\r?\n
admitted:\ (?p>date)\r?\n
released:\ (?p>date)$
```
### 子例程调用上的量词
***
子例程调用上的量词的作用与递归上的量词的作用相同。为了满足量词的要求,调用将按顺序重复多次。`([abc])(?1){3}` 匹配abcb和由a,b和c组成的任意一个长度为4的字符串。首先,组匹配一次,然后调用匹配三遍。此正则表达式等效于`([abc])[abc]{3}`。
子例程调用将忽略组中的量词。`([abc]){3}(?1)`也匹配abcb 。首先,该组匹配三遍,因为它有一个量词{3}。然后,子例程调用将匹配一次,因为它没有量词。`([abc]){3}(?1){3}`匹配六个字母,例如abbcab ,因为现在组和调用都重复了3次。这两个正则表达式等效于`([abc]){3}[abc]` 和`([abc]){3}[abc]{3}`。
尽管ruby不支持子例程定义组,但确实支持对重复零次的组的子例程调用。`(a){0}\g<1>{3}`与aaa匹配。该组本身被跳过,因为它被重复了零次。然后,子例程调用根据其量词匹配三遍。这在pcre 7.7及更高版本中也适用。由于存在错误,它在pcre的较旧版本或任何版本的perl中均无法(可靠地)工作。
患者记录示例的ruby版本可以进一步整理为:
```bash
(?'date'(?:3[01]|[12][0-9]|[1-9])
-(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)
-(?:19|20)[0-9][0-9]){0}
^name:\ (.*)\r?\n
born:\ \g'date'\r?\n
admitted:\ \g'date'\r?\n
released:\ \g'date'$