现在的位置: 首页 > 综合 > 正文

正则基础之——反向引用

2012年09月28日 ⁄ 综合 ⁄ 共 2001字 ⁄ 字号 评论关闭

1        概述

捕获组捕获到的内容,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用方式就是反向引用。要了解反向引用,首先要了解捕获组,关于捕获组,参考 正则基础之——捕获组(capture group

反向引用的作用通常是用来查找或限定重复、查找或限定指定标识配对出现等等。

对于普通捕获组和命名捕获组的引用,语法如下:

普通捕获组反向引用:\k<number>,通常简写为\number

命名捕获组反向引用:\k<name>或者\k'name'

普通捕获组反向引用中number是十进制的数字,即捕获组的编号;命名捕获组反向引用中的name为命名捕获组的组名。

2       反向引用匹配原理

捕获组(Expression)在匹配成功时,会将子表达式匹配到的内容,保存到内存中一个以数字编号的组里,可以简单的认为是对一个局部变量进行了赋值,这时就可以通过反向引用方式,引用这个局部变量的值。一个捕获组(Expression)在匹配成功之前,它的内容可以是不确定的,一旦匹配成功,它的内容就确定了,反向引用的内容也就是确定的了。

反向引用必然要与捕获组一同使用的,如果没有捕获组,而使用了反向引用的语法,不同语言的处理方式不一致,有的语言会抛异常,有的语言会当作普通的转义处理。

2.1     从一个简单例子说起

源字符串:abcdebbcde

正则表达式:([ab])\1

对于正则表达式“([ab])\1”,捕获组中的子表达式“[ab]”虽然可以匹配“a”或者“b”,但是捕获组一旦匹配成功,反向引用的内容也就确定了。如果捕获组匹配到“a”,那么反向引用也就只能匹配“a”,同理,如果捕获组匹配到的是“b”,那么反向引用也就只能匹配“b”。由于后面反向引用“\1”的限制,要求必须是两个相同的字符,在这里也就是“aa”或者“bb”才能匹配成功。

考察一下这个正则表达式的匹配过程,在位置0处,由“([ab])”匹配“a”成功,将捕获的内容保存在编号为1的组中,然后把控制权交给“\1”,由于此时捕获组已记录了捕获内容为“a”,“\1”也就确定只有匹配到“a”才能匹配成功,这里显然不满足,“\1”匹配失败,由于没有可供回溯的状态,整个表达式在位置0处匹配失败。

正则引擎向前传动,在位置5之前,“([ab])”一直匹配失败。传动到位置5处时,,“([ab])”匹配到“b”,匹配成功,将捕获的内容保存在编号为1的组中,然后把控制权交给“\1”,由于此时捕获组已记录了捕获内容为“b”,“\1”也就确定只有匹配到“b”才能匹配成功,满足条件,“\1”匹配成功,整个表达式匹配成功,匹配结果为“bb”,匹配开始位置为5,结束位置为7

扩展一下,正则表达式“([a-z])\1{2}”也就表达连续三个相同的小写字母。

2.2     一个复杂例子的分析

详细的分析讨论参考:正则表达式正向预搜索的问题

源字符串:aaa bbbb ffffff 999999999

正则表达式:(\w)((?=\1\1\1)(\1))+

测试代码:

string test = "aaa bbbb ffffff 999999999";

Regex reg = new Regex(@"(\w)((?=\1\1\1)(\1))+");

MatchCollection mc = reg.Matches(test);

foreach (Match m in mc)

{

      richTextBox2.Text += "匹配结果:" + m.Value.PadRight(12, ' ') + "匹配开始位置:" + m.Index + "\n";

}

//输出

匹配结果bb          匹配开始位置4

匹配结果:ffff        匹配开始位置:9

匹配结果:9999999     匹配开始位置:16

匹配结果分析:

正则表达式(\w)((?=\1\1\1)(\1))+从匹配结果上分析,其实就等价于 (\w)(\1)*(?=\1\1\1)(\1) ,这个会相对好理解一些,下面讨论下分析过程。

因为“+”等价于“{1,}”,表示至少匹配1次,下面把子表达式“((?=\1\1\1)(\1))+”展开来看下规律,下表中的次数表示子表达式“((?=\1\1\1)(\1))+”匹配成功的次数

次数

等价表达式

1

(\w)((?=\1\1\1)(\1))

2

(\w)((?=\1\1\1)(\1))((?=\1\1\1)(\1))

3

(\w)((?=\1\1\1)(\1))((?=\1\1\1)(\1))((?=\1\1\1)(\1))

如果最后一个“((?=\1\1\1)(\1))”匹配成功,那么中间的“((?=\1\1\1)(\1))”一定可以匹配成功,所以中间的限制条件(?=\1\1\1)就没有意义了,这时就可以简写为“(\1)”,也就是

次数

等价表达式

1

(\w)((?=\1\1\1)(\1))

2

抱歉!评论已关闭.