评论
标点的另一个用法是使用“(?#comment)”语法包含评论。一个更好的办法是设置“Ignore Pattern Whitespace”选项,它允许空白字符插入表达式然后当使用表达式时忽略它。设置了这个选项之后,任何文本每行末尾在数字符号“#”后面的东西都被忽略。例如,我们可以格式化先前的例子如下:
31. Text between HTML tags, with comments
(?<= # Search for a prefix, but exclude it
<(\w+)> # Match a tag of alphanumerics within angle brackets
) # End the prefix
.* # Match any text
(?= # Search for a suffix, but exclude it
<\/\1> # Match the previously captured tag preceded by "/"
) # End the suffix
贪婪与懒惰
当一个正则表达式有一个可以接受一个重复次数范围的量词(像“.*”),正常的行为是匹配尽可能多的字符。考虑下面的正则表达式:
32. a.*b -- The longest string starting with a and ending with b
如果这被用来搜索字符串“aabab”,它会匹配整个字符串“aabab”。这被称为“贪婪”匹配。有些时候,我们更喜欢“懒惰”匹配,其中一个匹配使用发现的最小数目的重复。表2中所有的量词可以增加一个问号“?”来转换到“懒惰”量词。这样,“*?”的意思就是“匹配任何数目的匹配,但是使用达到一个成功匹配的最小数目的重复”。现在让我们试试懒惰版本的例子(32):
33. a.*?b -- The shortest string starting with a and ending with b
如果我们把这个应用到相同的字符串“aabab”,它会先匹配“aab”然后匹配“ab”。
*? | 重复任意次数,但尽可能少 |
+? | 匹配一次或多次,但尽可能少 |
?? | 重复零次或多次,但尽可能少 |
{n,m}? | 重复最少n次,但不多于m次,但尽可能少 |
{n,}? | 重复最少n次,但尽可能少 |
表5 懒惰量词
我们遗漏了什么?
我已经描述了很多元素,使用它们来开始创建正则表达式;但是我还遗漏了一些东西,它们在下面的表中归纳出来。这些中的很多都在项目文件中使用额外的例子说明了。例子编号在这个表的左列中列出。
\a | 报警字符 | |
\b | 通常是单词边界,但是在一个字符集合中它表示退格键 | |
\t | 制表符 | |
34 | \r | 回车 |
\v | 垂直制表符 | |
\f | 分页符 | |
35 | \n | 换行符 |
\e | ESC | |
36 | \nnn | ASCII码八进制数为nnn的字符 |
37 | \xnn | 十六进制数为nn的字符 |
38 | \unnnn | Unicode码为nnnn的字符 |
39 | \cN | Control N字符,例如回车(Ctrl-M)就是\cM |
40 | \A | 字符串的开始(像^但是不依赖于多行选项) |
41 | \Z | 字符串的结尾或者\n之前的字符串结尾(忽略多行) |
\z | 字符串结尾(忽略多行) | |
42 | \G | 当前搜索的开始阶段 |
43 | \p{name} | 命名为name的Unicode类中的任何字符,例如\p{IsGreek} |
(?>exp) | 贪婪子表达式,也被称为非回溯子表达式。它只匹配一次然后就不再参与回溯。 | |
44 | (?<x>-<y>exp) or (?-<y>exp) | Balancing group. This is complicated but powerful. It allows named capture groups to be manipulated on a push down/pop up stack and can be used, for example, to search for matching parentheses, which is otherwise not possible with regular expressions. See the example in the project file. |
45 | (?im-nsx:exp) | 正则表达式选项为子表达式exp |
46 | (?im-nsx) | Change the regular expression options for the rest of the enclosing group |
(?(exp)yes
no) | The subexpression exp is treated as a zero-width positive lookahead. If it matches at this point, the subexpression yes becomes the next match, otherwise no is used. | |
(?(exp)yes) | Same as above but with an empty no expression | |
(?(name)yes
no) | This is the same syntax as the preceding case. If name is a valid group name, the yes expression is matched if the named group had a successful match, otherwise the no expression is matched. | |
47 | (?(name)yes) | Same as above but with an empty no expression |
表6 我们遗漏的东西。左端的列显示了项目文件中说明这个结构的例子的序号
结论
我们已经给出了很多例子来说明.NET正则表达式的关键特性,强调使用工具(如Expresso)来测试,实践,然后是用例子来学习。如果你想要深入的研究,网上也有很多在线资源会帮助你更深入的学习。你可以从访问Ultrapico网站开始。如果你想读一本相关书籍,我建议Jeffrey Friedl写的最新版的《Mastering Regular Expressions》。
Code Project中还有很多不错的文章,其中包含下面的教程:
·An Introduction to Regular Expressions by Uwe Keim
·Microsoft Visual C# .NET Developer’s Cookbook: Chapter on Strings and Regular Expressions