本节书摘来自异步社区《正则表达式经典实例(第2版)》一书中的第2章,第2.21节,作者: 【美】jan goyvaerts , steven levithan著,更多章节内容可以访问云栖社区“异步社区”公众号查看
问题描述
匹配任意10个数字的连续序列,如1234567890。并把这个序列转换成(美国)电话号码的常见格式,如(123) 456-7890,
解决方案
正则表达式
替代文本
讨论
使用捕获分组的替代
实例2.10讲解了在正则表达式中如何使用捕获分组来多次匹配相同的文本。在正则表达式中,每个捕获分组匹配到的文本在每次成功匹配之后都是可用的。你可以把部分或者所有捕获分组中的文本按照任意顺序甚至多次插入到替代文本中。
一些流派,如python和ruby,在正则表达式和替代文本中对于反向引用使用相同的语法«1»。其他流派使用的则是perl中的语法«$1»,也就是说使用的是美元符号而不是反斜杠。php对于两种语法都支持。
在perl中,«$1»以及更高编号的分组实际上都是变量,它们的值会在每次正则匹配成功之后进行设置。你可以在代码中的任意地方使用它们,直到下一次正则匹配开始。.net、java、javascript和php只在替代文本语法中支持«$1»。这些编程语言还提供了其他在代码中访问捕获分组的方式。这会在第3章中详细解释。
$10及更多分组
本书中的所有正则流派都支持在单个正则表达式中使用最多99个捕获分组。在替代文本中,对于«$10»或«10»以及更多的分组则会产生二义性。这些可以被解释为是第10个捕获分组,或者是第一个捕获分组后跟着一个字面上的0。
.net、xregexp、php和perl允许在数字周围使用花括号来澄清意图。«${10}» 总是代表第10个捕获分组,而«${1}0»则总是意味着第一个分组后跟着一个字面上的0。
java和javascript对于«$10»使用了更加聪明的处理办法。如果在你的正则表达式中存在这个两位数的捕获分组的话,那么两位数字都会用于引用捕获分组。如果并不存在这么多捕获分组的话,那么只有第一个数字用来引用分组,这样第二个数字就当作了字面字符。因此«$23»只有在它存在的时候才被认为是第23个捕获分组。否则,它被当作是第2个捕获分组后面跟着一个字面上的«3»。
.net、xregexp、php、perl、python和ruby总是把«$10»和«10»视为第10个捕获分组,而不管它们是否真的存在。如果它不存在的话,就会出现引用不存在的分组的状态。
对不存在的分组的引用
在这个实例的解决方案中的正则表达式拥有3个捕获分组。如果你在替代文本中输入了«$4»或«4»,就添加了一个对不存在的捕获分组的引用。这可能会触发如下的3种不同的行为。
java、xregexp和python会报错,并且抛出异常或者返回错误消息。因此不要在这些流派中使用无效的反向引用。(事实上,你不应当在任何流派中使用无效的反向引用。)如果想要添加的是字面上的«$4»或«4»,就需要对美元符号或反斜杠进行转义。实例2.19中对此有详细的解释。
php、perl和ruby会替换在替代文本中所有的反向引用,也包括了那些指向不存在的分组的引用。不存在的分组显然不会捕获任何文本,因此对它们的引用也就简单地被替换为空。
最后,.net和javascript(未使用xregexp时)则把对不存在分组的引用当作是替代文本中的字面文本。
如果某个分组在正则表达式中存在,匹配中却没有捕获到任何东西,无论是什么流派,它仍然可以在替换文本中使用,只不过它们真实的值是空串。
使用命名捕获的解决方案
支持命名捕获的流派
如果你在正则表达式中使用了命名捕获分组,那么在.net、java 7、xregexp、python和ruby 1.9中允许在替代文本中使用命名反向引用。在替代文本使用的命名反向引用语法与正则表达式中使用的不同。
ruby在替代文本中使用与其在正则表达式中一样的反向引用语法。对于ruby 1.9中的命名捕获分组,它的语法是«k»或«k'group'»。你可以选择尖括号也可以选择单引号,只看哪种方便。
perl 5.10及以后版本将命名捕获分组匹配的文本保存在散列变量(hash)%+中。你可以使用$+{name}获得名为“name”的分组匹配的文本。perl在替代文本中插入变量值,所以在替代文本中可以把«$+{name}»视为命名反向引用。
php(使用pcre)在正则表达式中支持命名捕获分组,但是在替代文本中则不支持。你可以在替代文本中使用编号的反向引用来引用正则表达式中的命名捕获分组。pcre会对所有命名和不命名的分组进行编号,顺序是从左向右。
.net、java 7、xregexp、python和ruby 1.9同样允许使用编号来引用命名分组。然而,.net对于命名分组则采用了不同的编号策略,这在实例2.11中已经进行了讲解。不推荐在.net、java 7、xregexp、python或ruby中混合使用命名和编号。要么所有分组都命名,要么所有分组都不命名。对于命名分组,应该总是使用命名的反向引用。