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

作者:lzmtw
 

 

6.使用?*或 进行重复

?:告诉引擎匹配前导字符0次或一次。事实上是表示前导字符是可选的。(问号)
  :告诉引擎匹配前导字符1次或多次(空格)
*:告诉引擎匹配前导字符0次或多次(星号)


<[A-Za-z][A-Za-z0-9]*> 匹配没有属性的HTML标签,“ <”以及“> ”是文字符号。

第一个字符集匹配一个字母,第二个字符集匹配一个字母或数字。

我们似乎也可以用 <[A-Za-z0-9] > 。但是它会匹配 <1> 。

但是这个正则表达式在你知道你要搜索的字符串不包含类似的无效标签时还是足够有效的。


限制性重复

许多现代的正则表达式实现,都允许你定义对一个字符重复多少次。


词法是:{min,max}。min和max都是非负整数。


如果逗号有而max被忽略了,则max没有限制。

如果逗号和max都被忽略了,则重复min次。

因此{0,}和*一样,{1,}和的作用一样。

你可以用 < <\b[1-9][0-9]{3}\b> > 匹配1000~9999之间的数字(“\b”表示单词边界)。 

             < <\b[1-9][0-9]{2,4}\b> > 匹配一个在100~99999之间的数字。


注意贪婪性

假设你想用一个正则表达式匹配一个HTML标签。你知道输入将会是一个有效的HTML文件,因此正则表达式不需要排除那些无效的标签。

所以如果是在两个尖括号之间的内容,就应该是一个HTML标签。


许多正则表达式的新手会首先想到用正则表达式 < < <. > > > ,他们会很惊讶的发现,对于测试字符串,

              “This is a <EM> first </EM> test”,

说明:<. >    第一个"<"表示第一字符为"<",第二个"."表示的是任意单个字符,第三个" "表示的是前面字符重复一次或多次,第四个">"表示最后是">"字符


你可能期望会返回 <EM> ,然后继续进行匹配的时候,返回 </EM> 。但事实是不会。


正则表达式将会匹配“ <EM> first </EM> ”。

很显然这不是我们想要的结果。原因在于“ ”是贪婪的。也就是说,“ ”会导致正则表达式引擎试图尽可能的重复前导字符。

尽量贪婪地匹配最后一个>)

只有当这种重复会引起整个正则表达式匹配失败的情况下,引擎会进行回溯。

也就是说,它会放弃最后一次的“重复”,然后处理正则表达式余下的部分。

和“ ”类似,“?*”的重复也是贪婪的。(word的通配符不是贪婪的


深入正则表达式引擎内部

让我们来看看正则引擎如何匹配前面的例子。

第一个记号是“ <”,这是一个文字符号。第二个符号是“.”,匹配了字符“E”,然后“ ”一直可以匹配其余的字符,直到一行的结束。

然后到了换行符,匹配失败(“.”不匹配换行符)。于是引擎开始对下一个正则表达式符号进行匹配。也即试图匹配“> ”。

到目前为止,“ <. ”已经匹配了“ <EM> first </EM> test”。引擎会试图将“> ”与换行符进行匹配,结果失败了。于是引擎进行回溯。


 结果是现在“ <. ”匹配“ <EM> first </EM> tes”。于是引擎将“> ”与“t”进行匹配。显然还是会失败。

这个过程继续,直到“ <. ”匹配“ <EM> first </EM”,“> ”与“> ”匹配。于是引擎找到了一个匹配“ <EM> first </EM> ”。

记住,正则导向的引擎是“急切的”,所以它会急着报告它找到的第一个匹配。而不是继续回溯,即使可能会有更好的匹配,例如“ <EM> ”。

所以我们可以看到,由于“ ”的贪婪性,使得正则表达式引擎返回了一个最左边的最长的匹配(即匹配"<EM> first </EM>",而不是匹配"<EM>")。



用懒惰性取代贪婪性

一个用于修正以上问题的可能方案是用“ ”的惰性代替贪婪性。你可以在“ ”后面紧跟一个问号“?”来达到这一点。

“*”,“{}”和“?”表示的重复也可以用这个方案。因此在上面的例子中我们可以使用“ <. ?> ”。


让我们再来看看正则表达式引擎的处理过程。

        “This is a <EM> first </EM> test”,          <. > 

再一次,正则表达式记号“ <”会匹配字符串的第一个“ <”。下一个正则记号是“.”。这次是一个懒惰的“ ”来重复上一个字符。

这告诉正则引擎,尽可能少的重复上一个字符。因此引擎匹配“.”和字符“E”,然后用“> ”匹配“M”,结果失败了。

引擎会进行回溯,和上一个例子不同,因为是惰性重复,所以引擎是扩展惰性重复而不是减少,于是“ <. ”现在被扩展为“ <EM”。引擎继续匹配下一个记号“> ”。

这次得到了一个成功匹配。引擎于是报告“ <EM> ”是一个成功的匹配。整个过程大致如此。

惰性扩展的一个替代方案我们还有一个更好的替代方案。可以用一个贪婪重复与一个取反字符集:“ <[^> ] > ”。

之所以说这是一个更好的方案在于使用惰性重复时,引擎会在找到一个成功匹配前对每一个字符进行回溯。而使用取反字符集则不需要进行回溯。

最后要记住的是,本教程仅仅谈到的是正则导向的引擎。

文本导向的引擎是不回溯的。但是同时他们也不支持惰性重复操作。


7.使用“.”匹配几乎任意字符

在正则表达式中,“.”是最常用的符号之一。不幸的是,它也是最容易被误用的符号之一。

“.”匹配一个单个的字符而不用关心被匹配的字符是什么。唯一的例外是新行符。


在本教程中谈到的引擎,缺省情况下都是不匹配新行符的。

因此在缺省情况下,“.”等于是字符集[^\n\r](Window)或[^\n](Unix)的简写。

这个例外是因为历史的原因。因为早期使用正则表达式的工具是基于行的。

它们都是一行一行的读入一个文件,将正则表达式分别应用到每一行上去。

在这些工具中,字符串是不包含新行符的。因此“.”也就从不匹配新行符。


现代的工具和语言能够将正则表达式应用到很大的字符串甚至整个文件上去。

本教程讨论的所有正则表达式实现都提供一个选项,可以使“.”匹配所有的字符,包括新行符。


在RegexBuddy,EditPadPro或PowerGREP等工具中,你可以简单的选中“点号匹配新行符”。

在Perl中,“.”可以匹配新行符的模式被称作“单行模式”。很不幸,这是一个很容易混淆的名词。

因为还有所谓“多行模式”。多行模式只影响行首行尾的锚定(anchor),而单行模式只影响“.”。

其他语言和正则表达式库也采用了Perl的术语定义。


当在.NETFramework中使用正则表达式类时,你可以用类似下面的语句来激活单行模式

          Regex.Match(“string”,”regex”,RegexOptions.SingleLine)


保守的使用点号“.”

点号可以说是最强大的元字符。它允许你偷懒:用一个点号,就能匹配几乎所有的字符。

但是问题在于,它也常常会匹配不该匹配的字符。我会以一个简单的例子来说明。


让我们看看如何匹配一个具有“mm/dd/yy”格式的日期,但是我们想允许用户来选择分隔符。

很快能想到的一个方案是 < <\d\d.\d\d.\d\d> > 。

看上去它能匹配日期“02/12/03”。问题在于02512703也会被认为是一个有效的日期。 


< <\d\d[-/.]\d\d[-/.]\d\d> > 看上去是一个好一点的解决方案。记住点号在一个字符集里不是元字符。这个方案远不够完善,它会匹配“99/99/99”。

 而 < <[0-1]\d[-/.][0-3]\d[-/.]\d\d> > 又更进一步。尽管他也会匹配“19/39/99”。


你想要你的正则表达式达到如何完美的程度取决于你想达到什么样的目的。如果你想校验用户输入,则需要尽可能的完美。

如果你只是想分析一个已知的源,并且我们知道没有错误的数据,用一个比较好的正则表达式来匹配你想要搜寻的字符就已经足够。



8.字符串开始和结束的锚定

锚定和一般的正则表达式符号不同,它不匹配任何字符。相反,他们匹配的是字符之前之后的位置。

(锚定:就是指定位置、定位。表明在什么位置,如:单词首位置还是单词尾的位置等等。word中图片的位置也有锚定的概念,就是定位图片位置。)


“^”匹配一行字符串第一个字符前的位置。 

        < <^a> > 将会匹配字符串“abc”中的a。 

        < <^b> > 将不会匹配“abc”中的任何字符。


类似的,$匹配字符串中最后一个字符的后面的位置。所以 

        < <c$> > 匹配“abc”中的c。


锚定的应用在编程语言中校验用户输入时,使用锚定是非常重要的。

如果你想校验用户的输入为整数,用 < <^\d $> > 。

用户输入中,常常会有多余的前导空格或结束空格。你可以用

        < <^\s*> > 和 < <\s*$> > 来匹配前导空格或结束空格。


使用“^”和“$”作为行的开始和结束锚定

如果你有一个包含了多行的字符串。例如:“firstline\n\rsecondline”(其中\n\r表示一个新行符)。

常常需要对每行分别处理而不是整个字符串。因此,几乎所有的正则表达式引擎都提供一个选项,可以扩展这两种锚定的含义。

“^”可以匹配字串的开始位置(在f之前),以及每一个新行符的后面位置(在\n\r和s之间)。

类似的,$会匹配字串的结束位置(最后一个e之后),以及每个新行符的前面(在e与\n\r之间)。


在.NET中,当你使用如下代码时,将会定义锚定匹配每一个新行符的前面和后面位置:

         Regex.Match("string","regex",RegexOptions.Multiline)

应用:stringstr=Regex.Replace(Original,"^","> ",RegexOptions.Multiline)--将会在每行的行首插入“> ”。


绝对锚定 < <\A> > 只匹配整个字符串的开始位置, < <\Z> > 只匹配整个字符串的结束位置。

即使你使用了“多行模式”, < <\A> > 和 < <\Z> > 也从不匹配新行符。


即使\Z和$只匹配字符串的结束位置,仍然有一个例外的情况。

如果字符串以新行符结束,则\Z和$将会匹配新行符前面的位置,而不是整个字符串的最后面。

这个“改进”是由Perl引进的,然后被许多的正则表达式实现所遵循,包括Java,.NET等。

如果应用 < <^[a-z] $> > 到“joe\n”,则匹配结果是“joe”而不是“joe\n”。


在本文中讲述了正则表达式中的组与向后引用,先前向后查看,条件测试,单词边界,选择符等表达式及例子,并分析了正则引擎在执行匹配时的内部机理。



9.单词边界

元字符 < <\b> > 也是一种对位置进行匹配的“锚”。这种匹配是0长度匹配。

有4种位置被认为是“单词边界”:
    1)在字符串的第一个字符前的位置(如果字符串的第一个字符是一个“单词字符”)
    2)在字符串的最后一个字符后的位置(如果字符串的最后一个字符是一个“单词字符”)
    3)在一个“单词字符”和“非单词字符”之间,其中“非单词字符”紧跟在“单词字符”之后
    4)在一个“非单词字符”和“单词字符”之间,其中“单词字符”紧跟在“非单词字符”后面


 “单词字符”是可以用“\w”匹配的字符,“非单词字符”是可以用“\W”匹配的字符。

在大多数的正则表达式实现中,“单词字符”通常包括 < <[a-zA-Z0-9_]> > 。例如:

          < <\b4\b> >

 能够匹配单个的4而不是一个更大数的一部分。这个正则表达式不会匹配“44”中的4。

换种说法,几乎可以说 < <\b> > 匹配一个“字母数字序列”的开始和结束的位置。

“单词边界”的取反集为 < <\B> > ,他要匹配的位置是两个“单词字符”之间或者两个“非单词字符”之间的位置。



深入正则表达式引擎内部

让我们看看把正则表达式 < <\bis\b> > 应用到字符串

                      “This island is  beautiful”。

引擎先处理符号 < <\b> > 。因为\b是0长度,所以第一个字符T前面的位置会被考察。

因为T是一个“单词字符”,而它前面的字符是一个空字符(void),所以\b匹配了单词边界。接着 < <i> > 和第一个字符“T”匹配失败。

匹配过程继续进行,直到第五个空格符,和第四个字符“s”之间又匹配了 < <\b> > 。然而空格符和 < <i> > 不匹配。

继续向后,到了第六个字符“i”,和第五个空格字符之间匹配了 < <\b> > ,然后 < <is> > 和第六、第七个字符都匹配了。

然而第八个字符和第二个“单词边界”不匹配,所以匹配又失败了。


到了第13个字符i,因为和前面一个空格符形成“单词边界”,同时 < <is> > 和“is”匹配。引擎接着尝试匹配第二个 < <\b> > 。

因为第15个空格符和“s”形成单词边界,所以匹配成功。引擎“急着”返回成功匹配的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值