作者:lzmtw |
6.使用?*或 进行重复 <[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>")。 一个用于修正以上问题的可能方案是用“ ”的惰性代替贪婪性。你可以在“ ”后面紧跟一个问号“?”来达到这一点。 “*”,“{}”和“?”表示的重复也可以用这个方案。因此在上面的例子中我们可以使用“ <. ?> ”。让我们再来看看正则表达式引擎的处理过程。 再一次,正则表达式记号“ <”会匹配字符串的第一个“ <”。下一个正则记号是“.”。这次是一个懒惰的“ ”来重复上一个字符。这告诉正则引擎,尽可能少的重复上一个字符。因此引擎匹配“.”和字符“E”,然后用“> ”匹配“M”,结果失败了。引擎会进行回溯,和上一个例子不同,因为是惰性重复,所以引擎是扩展惰性重复而不是减少,于是“ <. ”现在被扩展为“ <EM”。引擎继续匹配下一个记号“> ”。这次得到了一个成功匹配。引擎于是报告“ <EM> ”是一个成功的匹配。整个过程大致如此。 最后要记住的是,本教程仅仅谈到的是正则导向的引擎。文本导向的引擎是不回溯的。但是同时他们也不支持惰性重复操作。 “.”匹配一个单个的字符而不用关心被匹配的字符是什么。唯一的例外是新行符。 在本教程中谈到的引擎,缺省情况下都是不匹配新行符的。因此在缺省情况下,“.”等于是字符集[^\n\r](Window)或[^\n](Unix)的简写。 这个例外是因为历史的原因。因为早期使用正则表达式的工具是基于行的。它们都是一行一行的读入一个文件,将正则表达式分别应用到每一行上去。在这些工具中,字符串是不包含新行符的。因此“.”也就从不匹配新行符。 当在.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”。 你想要你的正则表达式达到如何完美的程度取决于你想达到什么样的目的。如果你想校验用户输入,则需要尽可能的完美。 如果你只是想分析一个已知的源,并且我们知道没有错误的数据,用一个比较好的正则表达式来匹配你想要搜寻的字符就已经足够。 “^”匹配一行字符串第一个字符前的位置。 < <^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)--将会在每行的行首插入“> ”。 9.单词边界 有4种位置被认为是“单词边界”: “单词字符”是可以用“\w”匹配的字符,“非单词字符”是可以用“\W”匹配的字符。 < <\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”形成单词边界,所以匹配成功。引擎“急着”返回成功匹配的结果。 |