.NET Framework 正则表达式
正则表达式提供了功能强大、灵活而又高效的方法来处理文本。正则表达式的全面模式匹配表示法使您可以快速地分析大量的文本以找到特定的字符模式;提取、编辑、替换或删除文本子字符串;或将提取的字符串添加到集合以生成报告。对于处理字符串(例如 HTML 处理、日志文件分析和 HTTP 标头分析)的许多应用程序而言,正则表达式是不可缺少的工具。
Microsoft .NET Framework 正则表达式并入了其他正则表达式实现的最常见功能,例如在 Perl 和 awk 中提供的那些功能。被设计为与 Perl 5 正则表达式兼容,.NET Framework 正则表达式还包括一些在其他实现中尚未提供的功能,例如从右到左匹配和即时编译。
.NET Framework 正则表达式类是基类库的一部分,并且可以和面向公共语言运行库的任何语言或工具(包括 ASP.NET 和 Visual Studio .NET)一起使用。
本节内容
- 正则表达式语言
- 提供正则表达式的编程语言方面的概述。
- 正则表达式类
- 提供阐释如何使用正则表达式类的信息和代码实例。
- 正则表达式行为的详细信息
- 提供有关 .NET Framework 正则表达式的功能和行为的信息。
- 正则表达式示例
- 提供用于阐释正则表达式的典型用法的代码实例。
相关章节
- 正则表达式语言元素
- 提供有关可用来定义正则表达式的字符集、运算符和构造的信息。
- System.Text.RegularExpressions
- 提供 .NET Framework System.Text.RegularExpressions 命名空间的类库参考信息。
为操纵文本,对正则表达式语言进行了精心设计和优化。正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。元字符使正则表达式具有处理能力。
您可能比较熟悉在 DOS 文件系统中使用的 ? 和 * 元字符,这两个元字符分别代表任意单个字符和字符组。DOS 文件命令 COPY *.DOC A:
命令文件系统将文件扩展名为 .DOC 的所有文件均复制到 A 驱动器的磁盘中。元字符 * 代表文件扩展名 .DOC 前的任何文件名。正则表达式极大地拓展了此基本思路,提供大量的元字符组,使通过相对少的字符描述非常复杂的文本匹配表达式成为可能。
例如,正则表达式 \s2000
在应用到文本正文时,将匹配在字符串“2000”前为任意空白字符(例如空格或制表符)的所有匹配项。
注意 如果使用的是 C++、C# 或 JScript,则在特殊转义符(例如 \s)之前必须加上附加的反斜杠(例如“\\s2000
”),以表明转义符中的反斜杠是原义字符。否则,正则表达式引擎会将\s
中的反斜杠和 s 当作两个单独的运算符来处理。如果使用的是 Visual Basic .NET,则不必添加反斜杠。如果使用的是 C#,则可以使用以 @ 为前缀并禁用转义的 C# 字符串(例如@"\s2000"
)。
正则表达式还可以执行更为复杂的搜索。例如,正则表达式 (?<char>\w)\k<char>
使用命名分组和反向引用来搜索相邻的成对字符。当应用于“I'll have a small coffee”这一字符串时,它将在单词“I'll”、“small”和“coffee”中找到匹配项。(有关此正则表达式的详细信息,请参见反向引用。)
以下各节详细介绍定义 .NET Framework 正则表达式语言的元字符组,以及说明如何使用正则表达式类来在您的应用程序中实现正则表达式。
以下各节介绍 .NET Framework 正则表达式类。
Regex
Regex 类表示不可变(只读)正则表达式类。它还包含各种静态方法,允许在不显式创建其他类的实例的情况下使用其他正则表达式类。
以下代码示例创建了 Regex 类的实例并在初始化对象时定义一个简单的正则表达式。请注意,使用了附加的反斜杠作为转义字符,它将 \s
匹配字符类中的反斜杠指定为原义字符。
[Visual Basic] ' Declare object variable of type Regex. Dim r As Regex ' Create a Regex object and define its regular expression. r = New Regex("\s2000") [C#] // Declare object variable of type Regex. Regex r; // Create a Regex object and define its regular expression. r = new Regex("\\s2000");
Match
Match 类表示正则表达式匹配操作的结果。以下示例使用 Regex 类的 Match 方法返回 Match 类型的对象,以便找到输入字符串中的第一个匹配项。此示例使用 Match 类的 Match.Success 属性来指示是否已找到匹配。
[Visual Basic] ' cCreate a new Regex object. Dim r As New Regex("abc") ' Find a single match in the input string. Dim m As Match = r.Match("123abc456") If m.Success Then ' Print out the character position where a match was found. ' (Character position 3 in this case.) Console.WriteLine("Found match at position " & m.Index.ToString()) End If [C#] // Create a new Regex object. Regex r = new Regex("abc"); // Find a single match in the string. Match m = r.Match("123abc456"); if (m.Success) { // Print out the character position where a match was found. // (Character position 3 in this case.) Console.WriteLine("Found match at position " + m.Index); }
MatchCollection
MatchCollection 类表示成功的非重叠匹配的序列。该集合为不可变(只读)的,并且没有公共构造函数。MatchCollection 的实例是由 Regex.Matches 属性返回的。
以下示例使用 Regex 类的 Matches 方法,通过在输入字符串中找到的所有匹配填充 MatchCollection。该示例将此集合复制到一个字符串数组和一个整数数组中,其中字符串数组用以保存每个匹配项,整数数组用以指示每个匹配项的位置。
[Visual Basic] Dim mc As MatchCollection Dim results(20) As String Dim matchposition(20) As Integer ' Create a new Regex object and define the regular expression. Dim r As New Regex("abc") ' Use the Matches method to find all matches in the input string. mc = r.Matches("123abc4abcd") ' Loop through the match collection to retrieve all ' matches and positions. Dim i As Integer For i = 0 To mc.Count - 1 ' Add the match string to the string array. results(i) = mc(i).Value ' Record the character position where the match was found. matchposition(i) = mc(i).Index Next i [C#] MatchCollection mc; String[] results = new String[20]; int[] matchposition = new int[20]; // Create a new Regex object and define the regular expression. Regex r = new Regex("abc"); // Use the Matches method to find all matches in the input string. mc = r.Matches("123abc4abcd"); // Loop through the match collection to retrieve all // matches and positions. for (int i = 0; i < mc.Count; i++) { // Add the match string to the string array. results[i] = mc[i].Value; // Record the character position where the match was found. matchposition[i] = mc[i].Index; }
GroupCollection
GroupCollection 类表示捕获的组的集合并返回单个匹配中捕获的组的集合。该集合为不可变(只读)的,并且没有公共构造函数。GroupCollection 的实例在 Match.Groups 属性返回的集合中返回。
以下控制台应用程序示例查找并输出由正则表达式捕获的组的数目。有关如何提取组集合的每一成员中的各个捕获项的示例,请参见下面一节的 Capture Collection 示例。
[Visual Basic] Imports System Imports System.Text.RegularExpressions Public Class RegexTest Public Shared Sub RunTest() ' Define groups "abc", "ab", and "b". Dim r As New Regex("(a(b))c") Dim m As Match = r.Match("abdabc") Console.WriteLine("Number of groups found = " _ & m.Groups.Count.ToString()) End Sub Public Shared Sub Main() RunTest() End Sub End Class [C#] using System; using System.Text.RegularExpressions; public class RegexTest { public static void RunTest() { // Define groups "abc", "ab", and "b". Regex r = new Regex("(a(b))c"); Match m = r.Match("abdabc"); Console.WriteLine("Number of groups found = " + m.Groups.Count); } public static void Main() { RunTest(); } }
该示例产生下面的输出。
[Visual Basic] Number of groups found = 3 [C#] Number of groups found = 3
CaptureCollection
CaptureCollection 类表示捕获的子字符串的序列,并且返回由单个捕获组执行的捕获的集合。由于限定符,捕获组可以在单个匹配中捕获多个字符串。Captures 属性(CaptureCollection 类的对象)是作为 Match 和 group 类的成员提供的,以便于对捕获的子字符串的集合的访问。
例如,如果使用正则表达式 ((a(b))c)+
(其中 + 限定符指定一个或多个匹配)从字符串“abcabcabc”中捕获匹配,则子字符串的每一匹配的 Group 的 CaptureCollection 将包含三个成员。
以下控制台应用程序示例使用正则表达式 (Abc)+
来查找字符串“XYZAbcAbcAbcXYZAbcAb”中的一个或多个匹配。该示例阐释了使用 Captures 属性来返回多组捕获的子字符串。
[Visual Basic] Imports System Imports System.Text.RegularExpressions Public Class RegexTest Public Shared Sub RunTest() Dim counter As Integer Dim m As Match Dim cc As CaptureCollection Dim gc As GroupCollection ' Look for groupings of "Abc". Dim r As New Regex("(Abc)+") ' Define the string to search. m = r.Match("XYZAbcAbcAbcXYZAbcAb") gc = m.Groups ' Print the number of groups. Console.WriteLine("Captured groups = " & gc.Count.ToString()) ' Loop through each group. Dim i, ii As Integer For i = 0 To gc.Count - 1 cc = gc(i).Captures counter = cc.Count ' Print number of captures in this group. Console.WriteLine("Captures count = " & counter.ToString()) ' Loop through each capture in group. For ii = 0 To counter - 1 ' Print capture and position. Console.WriteLine(cc(ii).ToString() _ & " Starts at character " & cc(ii).Index.ToString()) Next ii Next i End Sub Public Shared Sub Main() RunTest() End Sub End Class [C#] using System; using System.Text.RegularExpressions; public class RegexTest { public static void RunTest() { int counter; Match m; CaptureCollection cc; GroupCollection gc; // Look for groupings of "Abc". Regex r = new Regex("(Abc)+"); // Define the string to search. m = r.Match("XYZAbcAbcAbcXYZAbcAb"); gc = m.Groups; // Print the number of groups. Console.WriteLine("Captured groups = " + gc.Count.ToString()); // Loop through each group. for (int i=0; i < gc.Count; i++) { cc = gc[i].Captures; counter = cc.Count; // Print number of captures in this group. Console.WriteLine("Captures count = " + counter.ToString()); // Loop through each capture in group. for (int ii = 0; ii < counter; ii++) { // Print capture and position. Console.WriteLine(cc[ii] + " Starts at character " + cc[ii].Index); } } } public static void Main() { RunTest(); } }
此示例返回下面的输出结果。
[Visual Basic] Captured groups = 2 Captures count = 1 AbcAbcAbc Starts at character 3 Captures count = 3 Abc Starts at character 3 Abc Starts at character 6 Abc Starts at character 9 [C#] Captured groups = 2 Captures count = 1 AbcAbcAbc Starts at character 3 Captures count = 3 Abc Starts at character 3 Abc Starts at character 6 Abc Starts at character 9
Group
group 类表示来自单个捕获组的结果。因为 Group 可以在单个匹配中捕获零个、一个或更多的字符串(使用限定符),所以它包含 Capture 对象的集合。因为 Group 继承自 Capture,所以可以直接访问最后捕获的子字符串(Group 实例本身等价于 Captures 属性返回的集合的最后一项)。
Group 的实例是由 Match.Groups(groupnum) 属性返回的,或者在使用“(?<groupname>)”分组构造的情况下,是由 Match.Groups("groupname") 属性返回的。
以下代码示例使用嵌套的分组构造来将子字符串捕获到组中。
[Visual Basic] Dim matchposition(20) As Integer Dim results(20) As String ' Define substrings abc, ab, b. Dim r As New Regex("(a(b))c") Dim m As Match = r.Match("abdabc") Dim i As Integer = 0 While Not (m.Groups(i).Value = "") ' Copy groups to string array. results(i) = m.Groups(i).Value ' Record character position. matchposition(i) = m.Groups(i).Index i = i + 1 End While [C#] int[] matchposition = new int[20]; String[] results = new String[20]; // Define substrings abc, ab, b. Regex r = new Regex("(a(b))c"); Match m = r.Match("abdabc"); for (int i = 0; m.Groups[i].Value != ""; i++) { // Copy groups to string array. results[i]=m.Groups[i].Value; // Record character position. matchposition[i] = m.Groups[i].Index; }
此示例返回下面的输出结果。
[Visual Basic] results(0) = "abc" matchposition(0) = 3 results(1) = "ab" matchposition(1) = 3 results(2) = "b" matchposition(2) = 4 [C#] results[0] = "abc" matchposition[0] = 3 results[1] = "ab" matchposition[1] = 3 results[2] = "b" matchposition[2] = 4
以下代码示例使用命名的分组构造,从包含“DATANAME:VALUE”格式的数据的字符串中捕获子字符串,正则表达式通过冒号“:”拆分数据。
[Visual Basic] Dim r As New Regex("^(?<name>\w+):(?<value>\w+)") Dim m As Match = r.Match("Section1:119900") [C#] Regex r = new Regex("^(?<name>\\w+):(?<value>\\w+)"); Match m = r.Match("Section1:119900");
此正则表达式返回下面的输出结果。
[Visual Basic] m.Groups("name").Value = "Section1" m.Groups("value").Value = "119900" [C#] m.Groups["name"].Value = "Section1" m.Groups["value"].Value = "119900"
Capture
Capture 类包含来自单个子表达式捕获的结果。
以下示例在 Group 集合中循环,从 Group 的每一成员中提取 Capture 集合,并且将变量 posn 和 length 分别分配给找到每一字符串的初始字符串中的字符位置,以及每一字符串的长度。
[Visual Basic] Dim r As Regex Dim m As Match Dim cc As CaptureCollection Dim posn, length As Integer r = New Regex("(abc)*") m = r.Match("bcabcabc") Dim i, j As Integer i = 0 While m.Groups(i).Value <> "" ' Grab the Collection for Group(i). cc = m.Groups(i).Captures For j = 0 To cc.Count - 1 ' Position of Capture object. posn = cc(j).Index ' Length of Capture object. length = cc(j).Length Next j i += 1 End While [C#] Regex r; Match m; CaptureCollection cc; int posn, length; r = new Regex("(abc)*"); m = r.Match("bcabcabc"); for (int i=0; m.Groups[i].Value != ""; i++) { // Capture the Collection for Group(i). cc = m.Groups[i].Captures; for (int j = 0; j < cc.Count; j++) { // Position of Capture object. posn = cc[j].Index; // Length of Capture object. length = cc[j].Length; } }
本节包含了一些代码示例,用以阐释如何在常见应用程序中使用正则表达式。
本节内容
- 扫描 HREF
- 提供一个示例,该示例搜索一个输入字符串并输出所有 href="..." 值及其在字符串中的位置。
- 更改日期格式
- 提供一个示例,该示例将 mm/dd/yy 格式的日期替换为 dd-mm-yy 格式的日期。
- 提取 URL 信息
- 提供一个示例,该示例从包含 URL 的字符串中提取协议和端口号。例如,“http://www.contoso.com:8080/letters/readme.html”将返回“http:8080”。
- 清理输入字符串
- 提供一个示例,该示例从字符串中清除无效的非字母数字字符。
- 确认有效电子邮件格式
- 提供一个示例,您可用它来验证一个字符串是否是有效的电子邮件格式。
以下示例搜索输入字符串并输出所有 href=“...”值和它们在字符串中的位置。它执行此操作的方式为,首先构造编译的 Regex 对象,然后使用 Match 对象来循环访问字符串中的所有匹配。
在此示例中,元字符 \s
匹配任何空白字符,\S
匹配任何非空白字符。
[Visual Basic] Sub DumpHrefs(inputString As String) Dim r As Regex Dim m As Match r = New Regex("href\s*=\s*(?:""(?<1>[^""]*)""|(?<1>\S+))", _ RegexOptions.IgnoreCase Or RegexOptions.Compiled) m = r.Match(inputString) While m.Success Console.WriteLine("Found href " & m.Groups(1).Value _ & " at " & m.Groups(1).Index.ToString()) m = m.NextMatch() End While End Sub [C#] void DumpHrefs(String inputString) { Regex r; Match m; r = new Regex("href\\s*=\\s*(?:\"(?<1>[^\"]*)\"|(?<1>\\S+))", RegexOptions.IgnoreCase|RegexOptions.Compiled); for (m = r.Match(inputString); m.Success; m = m.NextMatch()) { Console.WriteLine("Found href " + m.Groups[1] + " at " + m.Groups[1].Index); } }
编译模式
在开始搜索字符串的循环前,此代码示例创建 Regex 对象来存储编译模式。因为需要花一些时间来分析、优化和编译正则表达式,所以在循环外执行这些任务,以便不重复这些任务。
Regex 类的实例是不可变的;每一实例对应于单个模式并且是无状态的。这将允许由不同的函数甚至是不同的线程共享单个 Regex 实例。
匹配结果类
搜索的结果存储在 Match 类中,这提供对该搜索提取的所有子字符串的访问。因为该类还记忆所搜索的字符串和所使用的正则表达式,所以它还可以使用这些字符串和表达式来在上一次搜索结束的地方开始另一个搜索。
显式命名的捕获
在传统的正则表达式中,捕获括号是自动按顺序编号的。这导致了两个问题。首先,如果通过插入或移除一组括号修改了一个正则表达式,则必须重写所有引用编号捕获的代码以反映新的编号。其次,因为不同的括号组经常被用来为可接受的匹配提供两个可替换的表达式,所以可能比较难于确定哪一个可替换的表达式实际返回了结果。
为了解决这些问题,Regex 支持将匹配捕获到指定的槽中的语法 (?<name>
...)。
(槽可以用字符串或整数命名;但整数可以被更快地回调。)因此,同一字符串的所有替换匹配都可被定向到同一位置。如果出现冲突,放置到槽中的最后一个匹配将是成功的匹配。(但是,单个槽的多个匹配的完整列表是可用的。有关详细信息,请参见 Group.Captures 集合。)
以下代码示例使用 Regex.Replace 方法来用 dd-mm-yy 的日期形式代替 mm/dd/yy 的日期形式。
[Visual Basic] Function MDYToDMY(input As String) As String Return Regex.Replace(input, _ "\b(?<month>\d{1,2})/(?<day>\d{1,2})/(?<year>\d{2,4})\b", _ "${day}-${month}-${year}") End Function [C#] String MDYToDMY(String input) { return Regex.Replace(input, "\\b(?<month>\\d{1,2})/(?<day>\\d{1,2})/(?<year>\\d{2,4})\\b", "${day}-${month}-${year}"); }
Regex 替换模式
本示例说明如何在 Regex.Replace 的替换模式中使用命名的反向引用。其中,替换表达式 ${day}
插入由 (?<day>
...)
组捕获的子字符串。
有几种静态函数使您可以在使用正则表达式操作时无需创建显式正则表达式对象,而 Regex.Replace 函数正是其中之一。如果您不想保留编译的正则表达式,这将给您带来方便。
以下代码示例使用 Match.Result 来从 URL 提取协议和端口号。例如,“http://www.contoso.com:8080/letters/readme.html”将返回“http:8080”。
[Visual Basic] Function Extension(url As String) As String Dim r As New Regex("^(?<proto>\w+)://[^/]+?(?<port>:\d+)?/", _ RegexOptions.Compiled) Return r.Match(url).Result("${proto}${port}") End Function [C#] String Extension(String url) { Regex r = new Regex(@"^(?<proto>\w+)://[^/]+?(?<port>:\d+)?/", RegexOptions.Compiled); return r.Match(url).Result("${proto}${port}"); }下面的代码示例使用静态 Regex.Replace 方法从字符串中抽出无效字符。您可以使用这里定义的
CleanInput
方法,清除掉在接受用户输入的窗体的文本字段中输入的可能有害的字符。CleanInput
在清除掉除 @、-(连字符)和 .(句点)以外的所有非字母数字字符后返回一个字符串。[Visual Basic] Function CleanInput(strIn As String) As String ' Replace invalid characters with empty strings. Return Regex.Replace(strIn, "[^\w\.@-]", "") End Function [C#] String CleanInput(string strIn) { // Replace invalid characters with empty strings. return Regex.Replace(strIn, @"[^\w\.@-]", ""); }下面的代码示例使用静态 Regex.IsMatch 方法验证一个字符串是否为有效电子邮件格式。如果字符串包含一个有效的电子邮件地址,则
IsValidEmail
方法返回 true,否则返回 false,但不采取其他任何操作。您可以使用IsValidEmail
,在应用程序将地址存储在数据库中或显示在 ASP.NET 页中之前,筛选出包含无效字符的电子邮件地址。[Visual Basic] Function IsValidEmail(strIn As String) As Boolean ' Return true if strIn is in valid e-mail format. Return Regex.IsMatch(strIn, ("^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$") End Function [C#] bool IsValidEmail(string strIn) { // Return true if strIn is in valid e-mail format. return Regex.IsMatch(strIn, @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$"); }