(全文转自 MSDN 知识库,用作学习笔记,原文地址:http://msdn.microsoft.com/zh-cn/library/ms972966.aspx,作者:Steven A. Smit)
正则表达式使用历史简介
正则表达式设计于五十年代,存在至今。正则表达式最初用于描述“正则集”,它们是一些神经生理学家研究的模式。正则表达式最早由数学家 Stephen Kleene 提出,最终由 Ken Thompson 在两种非常流行的文本实用程序 qed 和 grep 中使用。Jeffrey Friedl 在其著作“Mastering Regular Expressions (2nd edition)”中对此作了进一步阐述。建议那些希望更多了解正则表达式理论和历史的人看看这本书。
在最近的五十年中,正则表达式逐渐从模糊深奥的数学概念发展为在各类工具和软件包中应用的主要功能。尽管数十年来很多 UNIX 工具都支持正则表达式,但仅仅是近十年来,它才在大部分 Windows 开发者工具包中得到体现。在 Microsoft® Visual Basic® 6 或 Microsoft® VBScript 中,即使情况理想,正则表达式仍难以使用。但随着.NET Framework 的推行,正则表达式的支持发展到极点,所有 Microsoft 开发者和所有 .NET 语言都可以使用正则表达式。
那么,正则表达式究竟是什么呢?正则表达式是一种语言,它可以明确描述文本字符串中的模式。除了简单描述这些模式之外,正则表达式引擎通常可用于遍历匹配,并使用模式作为分隔符来将字符串解析为子字符串,或以智能方式替换文本或重新设置文本格式。正则表达式为解决与文本处理有关的许多常见任务提供了有效而简捷的方式。
在讨论正则表达式时,通常以正则表达式匹配(或不匹配)的文本为基础分析正则表达式。本文(以及 System.Text.RegularExpressions 类)将在正则表达式交互操作中引用 3 个参与对象:正则表达式的“模式”、“输入”字符串和字符串内的所有模式的“匹配”。
简单表达式
最简单的正则表达式大家都已熟悉,即文字字符串。特定的字符串可通过文字本身加以描述;像 foo 这样的正则表达式模式可精确匹配输入的字符串 foo。在本例中,也将匹配如下输入:**The food was quite tasty**,如果希望精确匹配,这可能不是预期结果。
当然,使用正则表达式匹配等于它自身的精确字符串是没有价值的实现,不能体现正则表达式的真正作用。假如不查找 foo,而是查找以字母 f 开头的所有单词,或所有 3 个字母的单词,那该怎么办?目前,这超出了文字字符串的合理范围。我们需要更加深入地研究正则表达式。下面是一个文字表达式示例及一些匹配的输入。
模式 输入(匹配) foo foo、food、foot、“There’s evil afoot.”
限定符
限定符提供了一种简单方法,用于指定在模式中允许特定字符或字符集自身重复出现的次数。有 3 个非显式限定符:
*****,描述“出现 0 或多次”。
+,描述“出现 1 或多次”。
?,描述“出现 0 或 1 次”。
限定符始终引用限定符前(左边)的模式,通常是单个字符,除非使用括号创建模式组。下面是一些模式示例及匹配的输入。
**模式** | **输入(匹配)** |
fo* | * foo*、*fo*e、*foo*d、*fooo*t、“*fo*rget it”、*f*unny、 pu*ff*y |
fo+ | * foo*、*fo*e、*foo*d、*foo*t、“*fo*rget it” |
fo? | * fo*o、*fo*e、*fo*od、*fo*ot、“*fo*rget it”、*f*unny、pu*ff*y |
模式 | 输入(匹配) |
ab{2}c | *abbc*、aa*abbc*cc |
ab{,2}c | *ac*、*abc*、*abbc*、a*abbc*c |
ab{2,3}c | *abbc*、*abbbc*、a*abbc*c、a*abbbc*c |
模式 | 输入(匹配) |
. | *a*、*b*、*c*、*1*、*2*、*3* |
.* | *Abc*, *123*, *任意字符串*, *无字符时也匹配* |
^c:\\ | *c:\windows*、*c:\\\\\*、*c:\foo.txt*、*c:\ 后跟任何其他内容* |
abc$ | *abc*、*123abc*、*以 abc 结束的任意字符串* |
(abc){2,3} | *abcabc*、*abcabcabc* |
模式 | 输入(匹配) |
^b[aeiou]t$ | *Bat*、*bet*、*bit*、*bot*、*but* |
^[0-9]{5}$ | *11111*, *12345*, *99999* |
^c:\\ | *c:\windows*、*c:\\\\\*、*c:\foo.txt*、*c:\ *后跟任何其他内容 |
abc$ | *abc*、*123abc*、*以 abc 结束的任意字符串* |
(abc){2,3} | *abcabc*、*abcabcabc* |
^[^-][0-9]$ | *0*、*1*、*2*、... (不匹配 -0、-1、 -2 等) |
**元字符** | **等效字符类** |
\a | 匹配铃声(警报);\u0007 |
\b | 匹配字符类外的字边界,它匹配退格字符,\u0008 |
\t | 匹配制表符,\u0009 |
\r | 匹配回车符,\u000D |
\w | 匹配垂直制表符,\u000B |
\f | 匹配换页符,\u000C |
\n | 匹配新行,\u000A |
\e | 匹配转义符,\u001B |
\040 | 匹配 3 位 8 进制 ASCII 字符。\040 表示空格(十进制数 32)。 |
\x20 | 使用 2 位 16 进制数匹配 ASCII 字符。此例中,\x2- 表示空格。 |
\cC | 匹配 ASCII 控制字符,此例中是 ctrl-C。 |
\u0020 | 使用 4 位 16 进制数匹配 Unicode 字符。此例中 \u0020 是空格。 |
\* | 不代表预定义字符类的任意字符都只作为该字符本身对待。因此,**\*** 等同于 **\x2A**(是文字 *,不是 * 元字符)。 |
\p{name} | 匹配已命名字符类“name”中的任意字符。支持名称是 Unicode 组和块范围。例如,Ll、Nd、Z、IsGreek、IsBoxDrawing 和 Sc(货币)。 |
\p{name} | 匹配已命名字符类“name”中不包括的文本。 |
\w | 匹配任意单词字符。对于非 Unicode 和 ECMAScript 实现,这等同于 **[a-zA-Z_0-9]**。在 Unicode 类别中,这等同于**[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}]**。 |
\W | \w 的否定,等效于 ECMAScript 兼容集合 **[^a-zA-Z_0-9]** 或 Unicode 字符类别 **[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}]**。 |
\s | 匹配任意空白区域字符。等效于 Unicode 字符类 **[\f\n\r\t\v\x85\p{Z}]**。如果使用 ECMAScript 选项指定 ECMAScript 兼容方式,\s 等效于 **[ \f\n\r\t\v]**(请注意前导空格)。 |
\S | 匹配任意非空白区域字符。等效于 Unicode 字符类别 **[^\f\n\r\t\v\x85\p{Z}]**。如果使用 ECMAScript 选项指定 ECMAScript 兼容方式,\S 等效于 **[^ \f\n\r\t\v]** (请注意 ^ 后的空格)。 |
\d | 匹配任意十进制数字。在 ECMAScript 方式下,等效于 Unicode 的 **[\p{Nd}]**、非 Unicode 的 **[0-9]**。 |
\D | 匹配任意非十进制数字。在 ECMAScript 方式下,等效于 Unicode 的 **[\p{Nd}]**、非 Unicode 的 **[^0-9]**。 |
表达式示例
很多人都喜欢通过示例学习,下面即提供一些表达式示例。要获取更多示例,请访问以下地址中的正则表达式联机数据库:http://www.regexlib.com/。
模式 | 说明 |
^\d{5}$ | 5 个数值数字,如美国邮政编码。 |
^(\d{5})|(\d{5}-\d{4}$ | 5 个数值数字或 5 个数字-短划线-4 个数字。匹配 5 位数字格式的美国邮政编码,或 5 位数字 + 4 位数字格式的美国邮政编码。 |
^(\d{5}(-\d{4})?$ | 与前一个相同,但更有效。使用 ? 可使模式中的 4 位数字成为可选部分,而不是要求分别比较不同的两个模式(通过另一种方式)。 |
^[+-]?\d+(\.\d+)?$ | 匹配任意有可选符号的实数。 |
^[+-]?\d*\.?\d*$ | 与上一个相同,但也匹配空字符串。 |
^(20|21|22|23|[01]\d)[0-5]\d$ | 匹配 24 小时制时间值。 |
/\*.*\*/ | 匹配 C 语言风格的注释 /* ... */ |
ASP.NET 中的验证
ASP.NET 提供了一套验证控件,与使用旧的(或愿意的话使用传统的) ASP 处理任务相比,验证控件使在 Web 窗体上验证输入变得非常容易。其中一个非常有效的验证器是 RegularExpressionValidator,如您所料,它允许您提供必须匹配输入的正则表达式来验证输入。设置控件的 ValidationExpression 属性可指定正则表达式的模式。下面显示了验证邮政代码字段的验证程序:
1 2 3 4 |
<asp:RegularExpressionValidator runat="server" id="ZipCodeValidator" ControlToValidate="ZipCodeTextBox" ErrorMessage="Invalid ZIP code format; format should be either 12345 or 12345-6789." ValidationExpression="(\d{5}(-\d{4})?" /> |
使用 RegularExpressionValidator 要注意几个问题:
决不要使用验证程序要验证的控件中的空字符串来激活验证器。只有 RequiredFieldValidator 才可以捕获空字符串。
您无需指定匹配字符的开始与结尾(^ 和$)- 它们是事先假设的。如果添加了开始与结尾,也没有任何影响,不需要这样做。
对于所有验证控件来说,必须在客户端以及服务器端进行验证。如果正则表达式不是 ECMAScript 兼容方式,客户端验证将失败。为了避免这种情况,确保表达式是 ECMAScript 兼容方式,否则只在服务器端进行控件验证。
正则表达式 API
除了 ASP.NET 验证控件,在.NET 中使用正则表达式的大多数情况都要使用 System.Text.RegularExpressions 命名空间中发现的类。特别是那些您希望熟悉的主类Regex、Match 和 MatchCollection。
顺便说一下,正则表达式缩写样式 regex 的发音究竟是 /reg-eks/ 还是 /rej-eks/,还有一些争议。本人倾向于后者,但两种发音都有专家赞同,所以选择哪个发音由您自己决定。
Regex 类有大量的方法和属性,如果您以前没有用过它,可能会感到无所适从。下面汇总了一些最常用的方法:
方法 | 说明 |
Escape / Unescape | 字符串中的转义元字符,用作表达式中的文字。 |
IsMatch | 如果正则表达式在输入字符串中发现匹配,返回“Ture”。 |
Match | 如果在输入字符串中发现匹配,则返回匹配对象。 |
Matches | 如果在输入字符串中发现包含任何或全部匹配,则返回匹配集合对象。 |
Replace | 用给定的替换字符串替换输入字符串中的匹配。 |
Split | 将输入字符串拆分成用正则表达式匹配分开的数组元素时,返回数组字符串。 |
除了指定很多方法外,还有一些选项可以指定,通常在 Regex 对象构造函数中。由于这些选项是位屏蔽的一部分,或许可以同时指定这些选项(如,可以同时指定 Multiline 和 Singleline)。
方法 | 说明 |
Compiled | 当在循环中执行许多匹配操作时使用此选项。这可以节省每一循环的分析表达式步骤。 |
Multiline | 它与输入字符串中的行数没有关系。确切地说,它只修改 **^** 和 **$** 的方式,以便匹配行开始 (BOL) 和行结尾 (EOL),而不是匹配整个输入字符串的开始和结尾。 |
IgnoreCase | 使模式在匹配搜索字符串时忽略大小写。 |
IgnorePatternWhitespace | 允许根据需要在模式中包括任意数量的空白区域,也支持使用 (?# 注释 #) 语法在模式中加入注释。 |
SingleLine | 它与输入字符串中的行数没有关系。更确切地说,它将导致 **.**(句点)元字符匹配任意字符,而不是除 \n 之外的任意字符(默认情况)。 |
使用正则表达式常执行的操作包括:验证、匹配和替换。大多数情况下,可以使用 Regex 类的静态方法完成这些操作,不需要实例化 Regex 类本身。要执行验证,全部要做的就是必建或找到正确的表达式,然后使用 Regex 类的 IsMatch() 方法将表达式应用到输入字符串中。例如,下面的函数演示了如何使用正则表达式验证邮政编码:
1 2 3 4 5 6 7 8 9 10 11 12 |
private void ValidateZipButton_Click(object sender, System.EventArgs e) { String ZipRegex = @"^\d{5}$"; if(Regex.IsMatch(ZipTextBox.Text, ZipRegex)) { ResultLabel.Text = "ZIP is valid!"; } else { ResultLabel.Text = "ZIP is invalid!"; } } |
类似的,可以使用静态 Replace() 方法将匹配替换为特定字符串,如下所示:
1
|
String newText = Regex.Replace(inputString, pattern, replacementText); |
最后,可以使用如下代码遍历输入字符串的匹配集合:
1 2 3 4 5 6 7 8 9 10 11 12 |
private void MatchButton_Click(object sender, System.EventArgs e) { MatchCollection matches = Regex.Matches(SearchStringTextBox.Text, MatchExpressionTextBox.Text); MatchCountLabel.Text = matches.Count.ToString(); MatchesLabel.Text = ""; foreach(Match match in matches) { MatchesLabel.Text += "Found" + match.ToString() + " at position " + match.Index + ".<br>"; } } |
通常,在您需要指定默认方式以外的方式时,需要实例化 Regex 类的实例。特别是在设置选项时。例如,要创建忽略大小写和模式空白区域的 Regex 实例,然后检索与该表达式匹配的集合,则应使用如下代码:
1 2 |
Regex re = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); MatchCollection mc = re.Matches(inputString); |
本文的下载文件中包括这些示例的完整使用版本,与简单 ASP.NET 页中的一样。
免费工具
Regulator (http://royo.is-a-geek.com/iserializable/regulator/) - 一种在客户端运行的正则表达式测试工具,通过 Web 服务与 RegexLib 紧密集成,提供对“匹配”、“拆分”和“替换”等的支持。包括性能分析和语法突出显示功能。
RegexDesigner.NET (http://www.sellsbrothers.com/tools/) - 一种功能强大的可视工具,可帮助构造并测试正则表达式。它可生成 C# 和/或 VB.NET 代码和已编译汇编代码,帮助您将表达式集成到应用程序中。
Regular Expression Workbench (v2.0) (http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=C712F2DF-B026-4D58-8961-4EE2729D7322) - Eric Gunnerson 开发的工具,用于创建、测试和研究正则表达式。具有“Examine-o-matic”功能,允许将鼠标悬停在正则表达式的上方,对其含义进行解码。
高级主题
正则表达式有两个不得不说的功能,一个是“命名组”,另一个是“四向处理”(lookaround processing)。由于这些功能很少使用,此处只简单阐述一下。
使用命名组,您可单独命名匹配组,然后在表达式中使用程序语言引用这些组。如果结合 Replace 方法重新设置输入字符串的格式(通过重新排列顺序、替换输入字符串中的元素),这个功能特别有效。例如,假设日期使用 MM/DD/YYYY 格式的字符串,而您希望日期格式是 DD-MM-YYYY。此时,可编写一个表达式捕获第一种格式,遍历它的匹配集合,并分析每个字符串,然后使用字符串操作建立替换字符串。这需要大量的代码和大量的处理。如果使用命名组,您可完成同样的任务,具体见下:
1 2 3 4 5 |
String MDYToDMY(String input) { return Regex.Replace(intput, @"\b(?<month>\d{1,2})/(?<day>\d{1,2}/(?<year>\d{4})\b", "${day}- ${month}-${year}"); } |
您还可以按编号或按名称引用组。在任何情况下,这种引用通称作“反向引用”。另一个经常使用反向引用的场合在匹配表达式本身,如下这个表达式用于查找重复的字母:[a-z]\1。它将匹配“aa”、“bb”、“cc”,但它不同于 [a-z]{2} 或 [a-z][a-z],后两者是等效的,后两者允许匹配“ab”或“ac”或任何其他两个字母的组合。反向引用允许表达式记住表达式已经分析并匹配过的输入字符串中的部分字符。
“四向处理”指很多正则表达式引擎所支持的正负 Lookahead 和 Lookbehind 功能。并不是所有的正则表达式引擎都支持验证四向处理。这些构造不使用字符,即使它们可以匹配字符。有些模式可能在不使用四向处理的情况下无法描述。特别是当模式中存在的一部分依赖于另一部分,如果不使用四向处理,则不能描述这样的模式。下面介绍了每个四向处理的语法。
语法 | 说明 |
(?=…) | 正 Lookahead |
(?!…) | 负 Lookahead |
(?<=…) | 正 Lookbehind |
(?<!…) | 负 Lookbehind |
密码验证是必需四向处理的一个示例。假定在密码限制中,密码必须介于 4 到 8 个字符,且必须至少包含一个数字。为此,您可以仅在匹配中测试 \d,然后使用字符串操作来测试长度。但要在正则表达式中实现这一切,必须使用 Lookahead。特别是正 lookahead,如下所示:^(?=.*\d).{4,8}$
结论
正则表达式是一种描述文本模式的极有效方法,它使文本模式成为字符串验证和操作的极好资源。.NET Framework 通过 System.Text.RegularExpressions 命名空间(特别是其中的 Regex 类)提供了对正则表达式的强大支持。使用 API 很简单,但编写正确的正则表达式通常比较费力。但幸运的是,正则表达式可以重复使用,您可以通过网络中的各种联机资源,从中找出其他人设计的表达式,或者在创建表达式遇到困难时得到帮助。
资源
正则表达式库 http://www.regexlib.com/
正则表达式讨论列表 http://aspadvice.com/login.aspx?ReturnUrl=%2fSignUp%2flist.aspx%3fl%3d68%26c%3d16&l=68&c=16
正则表达式论坛 http://forums.regexadvice.com/
正则表达式 Web 日志 http://blogs.regexadvice.com/
Mastering Regular Expressions (O’Reilly),作者 Jeffrey Friedl http://www.regex.info/
.NET 正则表达式参考 http://msdn.microsoft.com/library/en-us/cpref/html/frlrfSystemTextRegularExpressions.asp
Jscript 正则表达式语法 http://www.msdn.microsoft.com/library/en-us/script56/html/js56jsgrpRegExpSyntax.asp
正则表达式信息 http://www.regular-expressions.info/
作者简介
Steven A. Smith 是 Microsoft 在 ASP.NET 技术方面的最有价值专家 (MVP),是 ASPAlliance.com 和 DevAdvice.com 的总裁和所有者。此外,他也是 ASPSmith Ltd(一家重点提供 .NET 培训的公司)的所有者和首席教师。他撰写了两本著作:“ASP.NET Developer’s Cookbook”和“ASP.NET By Example”,并在 MSDN 和 AspNetPRO 杂志上发表了一些文章。Steve 每年都在各种会议上发表演讲,是 INETA 联络处的成员。Steve 拥有企业管理硕士学位及计算机科学工程理学士学位。
如果要联系 Steve,请发送电子邮件至 ssmith@aspalliance.com。