vb.net正则表达式快速入门(3)完

作者:lzmtw
 



10.选择符

正则表达式中“ ¦”表示选择。

你可以用选择符匹配多个可能的正则表达式中的一个。如果你想搜索文字“cat”或“dog”,你可以用 < <cat¦dog> > 。

如果你想有更多的选择,你只要扩展列表 < <cat¦dog¦mouse¦fish> > 。

选择符在正则表达式中具有最低的优先级,也就是说,它告诉引擎要么匹配选择符左边的所有表达式,要么匹配右边的所有表达式。

你也可以用圆括号来限制选择符的作用范围。如 < <\b(cat¦dog)\b> > ,这样告诉正则引擎把(cat ¦dog)当成一个正则表达式单位来处理。


注意正则引擎的“急于表功”性

正则引擎是急切的,当它找到一个有效的匹配时,它会停止搜索。因此在一定条件下,选择符两边的表达式的顺序对结果会有影响。

假设你想用正则表达式搜索一个编程语言的函数列表:Get,GetValue,Set或SetValue。一个明显的解决方案是

          < <Get¦GetValue¦Set¦SetValue> > 。

让我们看看当搜索SetValue时的结果。因为 < <Get> > 和 < <GetValue> > 都失败了,而 < <Set> > 匹配成功。

因为正则导向的引擎都是“急切”的,所以它会返回第一个成功的匹配,就是“Set”,而不去继续搜索是否有其他更好的匹配。

和我们期望的相反,正则表达式并没有匹配整个字符串。


有几种可能的解决办法。

    一是考虑到正则引擎的“急切”性,改变选项的顺序,例如我们使用

             < <GetValue¦Get¦SetValue¦Set> > ,

             这样我们就可以优先搜索最长的匹配。

    我们也可以把四个选项结合起来成两个选项:

             < <Get(Value)?¦Set(Value)?> > 。

             因为问号重复符是贪婪的,所以SetValue总会在Set之前被匹配。

     一个更好的方案是使用单词边界:

             < <\b(Get¦GetValue¦Set¦SetValue)\b> > 或 < <\b(Get(Value)?¦Set(Value)?\b> > 。

            更进一步,既然所有的选择都有相同的结尾,我们可以把正则表达式优化为 < <\b(Get¦Set)(Value)?\b> > 。



11.组与向后引用

把正则表达式的一部分放在圆括号内,你可以将它们形成组。然后你可以对整个组使用一些正则操作,例如重复操作符。

要注意的是,只有圆括号“()”才能用于形成组。“[]”用于定义字符集。“{}”用于定义重复操作。


当用“()”定义了一个正则表达式组后,正则引擎则会把被匹配的组按照顺序编号,存入缓存

当对被匹配的组进行向后引用的时候,可以用“\数字”的方式进行引用。

        < <\1> > 引用第一个匹配的后向引用组,

        < <\2> > 引用第二个组,以此类推,

        < <\n> > 引用第n个组。

    而 < <\0> > 则引用整个被匹配的正则表达式本身


我们看一个例子。假设你想匹配一个HTML标签的开始标签和结束标签,以及标签中间的文本。比如

         <B>This is a test</B> ,

我们要匹配 <B> 和 </B> 以及中间的文字。我们可以用如下正则表达式:

       “ <([A-Z][A-Z0-9]*)[^> ]*> .*? </\1> ”

首先,“ <”将会匹配“ <B> ”的第一个字符“ <”。

然后[A-Z]匹配B,[A-Z0-9]*将会匹配0到多次字母数字,后面紧接着0到多个非“> ”的字符。

最后正则表达式的“> ”将会匹配“ <B> ”的“> ”。

接下来正则引擎将对结束标签之前的字符进行惰性匹配,直到遇到一个“ </”符号。

然后正则表达式中的“\1”表示对前面匹配的组“([A-Z][A-Z0-9]*)”进行引用,在本例中,被引用的是标签名“B”

所以需要被匹配的结尾标签为“ </B> ”你可以对相同的后向引用组进行多次引用, 

        < <([a-c])x\1x\1> >

将匹配“axaxa”、“bxbxb”以及“cxcxc”。如果用数字形式引用的组没有有效的匹配,则引用到的内容简单的为空。


一个后向引用不能用于它自身

              < <([abc]\1)> > 是错误的。

因此你不能将 < <\0> > 用于一个正则表达式匹配本身,它只能用于替换操作中。


后向引用不能用于字符集内部

           < <(a)[\1b]> > 中的 < <\1> > 并不表示后向引用。在字符集内部, < <\1> > 可以被解释为八进制形式的转码。

向后引用会降低引擎的速度,因为它需要存储匹配的组。如果你不需要向后引用,你可以告诉引擎对某个组不存储。例如:

           < <Get(?:Value)> > 。其中“(”后面紧跟的“?:”会告诉引擎对于组(Value),不存储匹配的值以供后向引用。


重复操作与后向引用

当对组使用重复操作符时,缓存里后向引用内容会被不断刷新,只保留最后匹配的内容。例如:

          < <([abc] )=\1> > 将匹配“cab=cab”,但是

          < <([abc]) =\1> > 却不会。

因为([abc])第一次匹配“c”时,“\1”代表“c”;然后([abc])会继续匹配“a”和“b”。最后“\1”代表“b”,所以它会匹配“cab=b”。


应用:检查重复单词--当编辑文字时,很容易就会输入重复单词,例如

         “the the”。

使用 < <\b(\w )\s \1\b> > 可以检测到这些重复单词。要删除第二个单词,只要简单的利用替换功能替换掉“\1”就可以了。


组的命名和引用

在PHP,Python中,可以用 < <(?P <name> group)> > 来对组进行命名。

在本例中,词法?P <name> 就是对组(group)进行了命名。其中name是你对组的起的名字。你可以用(?P=name)进行引用。


.NET的命名组.NETframework也支持命名组。不幸的是,微软的程序员们决定发明他们自己的语法,而不是沿用Perl、Python的规则

目前为止,还没有任何其他的正则表达式实现支持微软发明的语法。


下面是.NET中的例子:

         (? <first> group)(?’second’group)

正如你所看到的,.NET提供两种词法来创建命名组:一是用尖括号“ <> ”,或者用单引号“’’”。

尖括号在字符串中使用更方便,单引号在ASP代码中更有用,因为ASP代码中“ <> ”被用作HTML标签。

要引用一个命名组,使用\k <name> 或\k’name’.当进行搜索替换时,你可以用“${name}”来引用一个命名组。



12.正则表达式的匹配模式

本教程所讨论的正则表达式引擎都支持三种匹配模式: 

            < </i> > 使正则表达式对大小写不敏感, 

            < </s> > 开启“单行模式”,即点号“.”匹配新行符

            < </m> > 开启“多行模式”,即“^”和“$”匹配新行符的前面和后面的位置。


在正则表达式内部打开或关闭模式

如果你在正则表达式内部插入修饰符(?ism),则该修饰符只对其右边的正则表达式起作用。

(?-i)是关闭大小写不敏感。你可以很快的进行测试。

             < <(?i)te(?-i)st> > 

应该匹配TEst,但是不能匹配teST或TEST.

 


13.原子组与防止回溯

在一些特殊情况下,因为回溯会使得引擎的效率极其低下。
让我们看一个例子:要匹配这样的字串,字串中的每个字段间用逗号做分隔符,第12个字段由P开头。我们容易想到这样的正则表达式

              < <^(.*?,){11}P> > 。

这个正则表达式在正常情况下工作的很好。但是在极端情况下,如果第12个字段不是由P开头,则会发生灾难性的回溯。

如要搜索的字串为

             “1,2,3,4,5,6,7,8,9,10,11,12,13”。

首先,正则表达式一直成功匹配直到第12个字符。

这时,前面的正则表达式消耗的字串为“1,2,3,4,5,6,7,8,9,10,11,”,到了下一个字符, < <P> > 并不匹配“12”。

所以引擎进行回溯,这时正则表达式消耗的字串为“1,2,3,4,5,6,7,8,9,10,11”。

继续下一次匹配过程,下一个正则符号为点号 < <.> > ,可以匹配下一个逗号“,”。

然而 < <,> > 并不匹配字符“12”中的“1”。匹配失败,继续回溯。大家可以想象,这样的回溯组合是个非常大的数量。因此可能会造成引擎崩溃。


用于阻止这样巨大的回溯有几种方案

    一种简单的方案是尽可能的使匹配精确。

        用取反字符集代替点号。例如我们用如下正则表达式 < <^([^,\r\n]*,){11}P> > ,这样可以使失败回溯的次数下降到11次。

    另一种方案是使用原子组。

        原子组的目的是使正则引擎失败的更快一点。因此可以有效的阻止海量回溯。


原子组的语法是 < <(?> 正则表达式)> > 。

位于(?> )之间的所有正则表达式都会被认为是一个单一的正则符号。

一旦匹配失败,引擎将会回溯到原子组前面的正则表达式部分。前面的例子用原子组可以表达成 < <^(?> (.*?,){11})P> > 。

一旦第十二个字段匹配失败,引擎回溯到原子组前面的 < <^> > 。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值