分组构造
分组构造描述了正则表达式的子表达式,用于捕获输入字符串的子字符串。 您可以使用分组构造来完成下列任务:
匹配输入字符串中重复的子表达式。
将限定符应用于拥有多个正则表达式语言元素的子表达式。 有关限定符的更多信息,请参见限定符。
包括由 Regex.Replace 和 Match.Result 方法返回的字符串的子表达式。
从 Match.Groups 属性中检索各个子表达式,并分别从匹配的文本作为一个整体处理它们。
.NET Framework 支持以下正则表达式分组构造:
匹配的子表达式
命名匹配的子表达式
平衡组定义
非捕获组
组选项
零宽度正预测先行断言
零宽度负预测先行断言
零宽度正回顾后发断言
零宽度负回顾后发断言
非回溯子表达式
分组构造和正则表达式对象
匹配的子表达式
以下的分组构造捕获匹配的子表达式:
( 子表达式 )
其中子表达式为任何有效正则表达式模式。 使用括号的捕获按正则表达式中左括号的顺序从一开始从左到右自动编号。 捕获元素编号为零的捕获是由整个正则表达式模式匹配的文本。
注意 |
---|
如果正则表达式模式匹配方法的 RegexOptions 参数包括 RegexOptions.ExplicitCapture 标志,或者如果 n 选项应用于此子表达式(参见本主题后面的 组选项),则不会捕获匹配的子表达式。 |
可以四种方法访问捕获的组:
通过使用正则表达式中的反向引用构造。 匹配的子表达式在相同的正则表达式中引用,方法是使用语法 \数字,其中数字是捕获的表达式的初始数字。
通过使用正则表达式中被命名的反向引用构造。 匹配的子表达式在相同的正则表达式中引用,方法是使用语法 \k<数字>,其中数字是捕获的表达式的初始数字。 捕获子表达式都有与原始编号相同的默认名称。 有关更多信息,请参见本主题后面的命名匹配的子表达式:
通过使用 Regex.Replace 或 Match.Result 方法调用中的 $ 编号 r 替换序列,其中 编号为捕获的字表达式的序号。
以编程的方式,通过使用 GroupCollection 对象的方式,该对象由 Match.Groups 属性返回。 集合中位置零上的成员表示正则表达式匹配。 每个后续成员表示匹配的子表达式。 有关更多信息,请参见分组构造和正则表达式对象一节。
以下示例阐释表示文本中重复单词的正则表达式。 正则表达式模式的两个捕获组表示重复的单词的两个实例。 第二个实例被捕获,以报告它在输入字符串的起始位置。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(\w+)\s(\1)\W"
Dim input As String = "He said that that was the the correct answer."
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine("Duplicate '{0}' found at positions {1} and {2}.", _
match.Groups(1).Value, match.Groups(1).Index, match.Groups(2).Index)
Next
End Sub
End Module
' The example displays the following output:
' Duplicate 'that' found at positions 8 and 13.
' Duplicate 'the' found at positions 22 and 26.
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"(\w+)\s(\1)";
string input = "He said that that was the the correct answer.";
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
Console.WriteLine("Duplicate '{0}' found at positions {1} and {2}.",
match.Groups[1].Value, match.Groups[1].Index, match.Groups[2].Index);
}
}
// The example displays the following output:
// Duplicate 'that' found at positions 8 and 13.
// Duplicate 'the' found at positions 22 and 26.
正则表达式模式为:
(\w+)\s(\1)\W
下表演示了如何解释正则表达式模式。
模式 |
说明 |
---|---|
(\w+) |
匹配一个或多个单词字符。 这是第一个捕获组。 |
\s |
与空白字符匹配。 |
(\1) |
与第一个捕获组捕获的子字符串匹配。 这是第二个捕获组。 该示例将其指定到捕获组上,从而重复单词的开始位置可从 Match.Index 属性检测。 |
\W |
匹配包括空格和标点符号的一个非单词字符。 这样可以防止正则表达式模式匹配从第一个捕获组的单词开头的单词。 |
命名匹配的子表达式
以下的分组构造捕获匹配的子表达式,并允许您按名称或数访问它:
(?<name>subexpression)
或:
(?'name' subexpression)
其中名称是有效的组名称,而子表达式是任何有效的正则表达式模式。 名称不得包含任何标点符号字符,并且不能以数字开头。
注意 |
---|
如果正则表达式模式匹配方法的 RegexOptions 参数包括 RegexOptions.ExplicitCapture 标志,或者如果 n 选项应用于此子表达式(参见本主题后面的 组选项),则捕获子表达式的唯一方法就是隐式命名捕获组。 |
可用以下方式访问已命名的捕获组:
通过使用正则表达式中被命名的反向引用构造。 使用语法 \k<name> 在同一正则表达式中引用匹配的子表达式,其中 name 是捕获子表达式的名称。
通过使用正则表达式中的反向引用构造。 匹配的子表达式在相同的正则表达式中引用,方法是使用语法 \数字,其中数字是捕获的表达式的初始数字。 已命名的匹配子表达式在匹配子表达式后从左到右连续编号。
通过使用 Regex.Replace 或 Match.Result 方法调用中的 ${名称} 替换顺序,其中名称为捕获的字表达式的名称。
通过使用 Regex.Replace 或 Match.Result 方法调用中的 $ 编号 > 替换序列,其中编号为捕获的子表达式的序号。
以编程的方式,通过使用 GroupCollection 对象的方式,该对象由 Match.Groups 属性返回。 集合中位置零上的成员表示正则表达式匹配。 每个后续成员表示匹配的子表达式。 已命名的捕获组在集合中存储在已编号的捕获组后面。
以编程方式,通过提供子表达式至 GroupCollection 对象的索引器(以 C#),或者至其 Item 属性(以 Visual Basic)的方式。
简单的正则表达式模式会阐释如何编号(未命名),并且可以以编程方式或通过正则表达式语言语法引用已命名的组。 正则表达式 ((?<One>abc)\d+)?(?<Two>xyz)(.*) 按编号和名称产生下列捕获组。 编号为 0 的第一个捕获组总是指整个模式。
Number |
Name |
模式 |
---|---|---|
0 |
0(默认名称) |
((?<One>abc)\d+)?(?<Two>xyz)(.*) |
1 |
1(默认名称) |
((?<One>abc)\d+) |
2 |
2(默认名称) |
(.*) |
3 |
一个 |
(?<One>abc) |
4 |
两个 |
(?<Two>xyz) |
下面的示例阐释了一个正则表达式,标识出重复的单词和紧随每个重复的单词的单词。 正则表达式模式定义了两个命名的子表达式:duplicateWord,它表示重复的单词 ;和 nextWord,它表示后面跟随重复单词的单词。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(?<duplicateWord>\w+)\s\k<duplicateWord>\W(?<nextWord>\w+)"
Dim input As String = "He said that that was the the correct answer."
Console.WriteLine(Regex.Matches(input, pattern, RegexOptions.IgnoreCase).Count)
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine("A duplicate '{0}' at position {1} is followed by '{2}'.", _
match.Groups("duplicateWord").Value, match.Groups("duplicateWord").Index, _
match.Groups("nextWord").Value)
Next
End Sub
End Module
' The example displays the following output:
' A duplicate 'that' at position 8 is followed by 'was'.
' A duplicate 'the' at position 22 is followed by 'correct'.
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"(?<duplicateWord>\w+)\s\k<duplicateWord>\W(?<nextWord>\w+)";
string input = "He said that that was the the correct answer.";
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
Console.WriteLine("A duplicate '{0}' at position {1} is followed by '{2}'.",
match.Groups["duplicateWord"].Value, match.Groups["duplicateWord"].Index,
match.Groups["nextWord"].Value);
}
}
// The example displays the following output:
// A duplicate 'that' at position 8 is followed by 'was'.
// A duplicate 'the' at position 22 is followed by 'correct'.
正则表达式模式按如下方式定义:
(?<duplicateWord>\w+)\s\k<duplicateWord>\W(?<nextWord>\w+)
下表演示了正则表达式的含义。
模式 |
说明 |
---|---|
(?<duplicateWord>\w+) |
匹配一个或多个单词字符。 将捕获组 duplicateWord 命名。 |
\s |
与空白字符匹配。 |
\k<duplicateWord> |
匹配名为 duplicateWord 的捕获的组。 |
\W |
匹配包括空格和标点符号的一个非单词字符。 这样可以防止正则表达式模式匹配从第一个捕获组的单词开头的单词。 |
(?<nextWord>\w+) |
匹配一个或多个单词字符。 将捕获组 nextWord 命名。 |
平衡组定义
平衡组删除先前定义的组的定义,并在当前组中存储先前定义的组和当前组之间的间隔。 此分组构造具有以下格式:
(?<name1-name2>subexpression)
或:
(?'name1-name2' subexpression)
name1 位置是当前的组(可选),name2 是一个以前定义的组,而子表达式是任何有效的正则表达式模式。 平衡组定义删除 name2 的定义并在 name1 中保存 name2 和 name1 之间的间隔。 如果未定义 name2 组,则匹配将回溯。 由于删除 name2 的最后一个定义会显示 name2 的先前定义,因此该构造允许将 name2 组的捕获堆栈用作计数器,用于跟踪嵌套构造(如括号或者左括号和右括号)。
平衡组定义将 name2 作为堆栈使用。 每个嵌套构造的开头字符放在组中,并在其 Group.Captures 集合中。 当匹配结束字符时,从组中删除其相应的开始字符,并且 Captures 集合减少 1。 所有嵌套构造的开始和结束字符匹配完后,name1 为空。
注意 |
---|
通过修改下面示例中的正则表达式来使用合适的嵌套构造的开始和结束字符后,你可以用它来处理多数嵌套构造,如数学表达式或包括多个嵌套方法调用的程序代码行。 |
下面的示例使用平衡组定义匹配输入字符串中的左右尖括号 (<>)。 该示例定义两个已命名的组,Open 和 Close,用作堆栈来跟踪配对的尖括号。 每个已捕获的左尖括被号推入到 Open 组的捕获集合,而每个已捕获的右尖括号被推入到 Close 组的捕获集合。 平衡组定义确保每个左尖括号都有一个匹配的右尖角括号。 如果没有,则最终子模式 (?(Open)(?!)) 仅会在 Open 组不为空的情况下求值(因此,如果所有嵌套构造尚未关闭)。 如果计算了最终子模式的值,则会匹配失败,因为输入字符串不应包含子字符串 ?! 的任何匹配项。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^[^<>]*" & _
"(" + "((?'Open'<)[^<>]*)+" & _
"((?'Close-Open'>)[^<>]*)+" + ")*" & _
"(?(Open)(?!))$"
Dim input As String = "<abc><mno<xyz>>"
Dim rgx AS New Regex(pattern)'
Dim m As Match = Regex.Match(input, pattern)
If m.Success Then
Console.WriteLine("Input: ""{0}"" " & vbCrLf & "Match: ""{1}""", _
input, m)
Dim grpCtr As Integer = 0
For Each grp As Group In m.Groups
Console.WriteLine(" Group {0}: {1}", grpCtr, grp.Value)
grpCtr += 1
Dim capCtr As Integer = 0
For Each cap As Capture In grp.Captures
Console.WriteLine(" Capture {0}: {1}", capCtr, cap.Value)
capCtr += 1
Next
Next
Else
Console.WriteLine("Match failed.")
End If
End Sub
End Module
' The example displays the following output:
' Input: "<abc><mno<xyz>>"
' Match: "<abc><mno<xyz>>"
' Group 0: <abc><mno<xyz>>
' Capture 0: <abc><mno<xyz>>
' Group 1: <mno<xyz>>
' Capture 0: <abc>
' Capture 1: <mno<xyz>>
' Group 2: <xyz
' Capture 0: <abc
' Capture 1: <mno
' Capture 2: <xyz
' Group 3: >
' Capture 0: >
' Capture 1: >
' Capture 2: >
' Group 4:
' Group 5: mno<xyz>
' Capture 0: abc
' Capture 1: xyz
' Capture 2: mno<xyz>
using System;
using System.Text.RegularExpressions;
class Example
{
public static void Main()
{
string pattern = "^[^<>]*" +
"(" +
"((?'Open'<)[^<>]*)+" +
"((?'Close-Open'>)[^<>]*)+" +
")*" +
"(?(Open)(?!))$";
string input = "<abc><mno<xyz>>";
Match m = Regex.Match(input, pattern);
if (m.Success == true)
{
Console.WriteLine("Input: \"{0}\" \nMatch: \"{1}\"", input, m);
int grpCtr = 0;
foreach (Group grp in m.Groups)
{
Console.WriteLine(" Group {0}: {1}", grpCtr, grp.Value);
grpCtr++;
int capCtr = 0;
foreach (Capture cap in grp.Captures)
{
Console.WriteLine(" Capture {0}: {1}", capCtr, cap.Value);
capCtr++;
}
}
}
else
{
Console.WriteLine("Match failed.");
}
}
}
// The example displays the following output:
// Input: "<abc><mno<xyz>>"
// Match: "<abc><mno<xyz>>"
// Group 0: <abc><mno<xyz>>
// Capture 0: <abc><mno<xyz>>
// Group 1: <mno<xyz>>
// Capture 0: <abc>
// Capture 1: <mno<xyz>>
// Group 2: <xyz
// Capture 0: <abc
// Capture 1: <mno
// Capture 2: <xyz
// Group 3: >
// Capture 0: >
// Capture 1: >
// Capture 2: >
// Group 4:
// Group 5: mno<xyz>
// Capture 0: abc
// Capture 1: xyz
// Capture 2: mno<xyz>
正则表达式模式为:
^[^<>]*(((?'Open'<)[^<>]*)+((?'Close-Open'>)[^<>]*)+)*(?(Open)(?!))$
正则表达式按如下方式解释:
模式 |
说明 |
---|---|
^ |
从字符串的开头部分开始。 |
[^<>]* |
匹配零个或多个不是左侧或右侧角度方括号的字符。 |
(?'Open'<) |
匹配左尖括号并分配给名为 Open 的组。 |
[^<>]* |
匹配零个或多个不是左侧或右侧角度方括号的字符。 |
((?'Open'<)[^<>]*) + |
匹配跟在非左尖括号或非右尖括号的零个或多个字符后面的一个或多个左尖括号匹配项。 这是第二个捕获组。 |
(?'Close-Open'>) |
匹配右尖括号,将 Open 组和当前组分配给 Close 组并删除 Open 组的定义。 |
[^<>]* |
匹配非左尖括号或非右尖括号的任何字符的零个或多个匹配项。 |
((?'Close-Open'>)[^<>]*)+ |
匹配跟在零后面或跟在非左尖括号或非右尖括号的多个字符后面的一个或多个右尖括号匹配项。 在匹配右尖括号时,将 Open 组和当前组分配给 Close 组并删除 Open 组的定义。 这是第三个捕获组。 |
(((?'Open'<)[^<>]*)+((?'Close-Open'>)[^<>]*)+)* |
匹配零个或多个下列模式的匹配项:一个或多个左尖括号匹配项,后跟零个或多个非尖括号字符,后跟一个或多个右尖括号的匹配项,后跟零个或多个非尖括号的匹配项。 在匹配右尖括号时,删除 Open 组的定义,并将 Open 组和当前组之间的子字符串分配给 Close 组。 这是第一个捕获组。 |
(?(Open)(?!)) |
如果 Open 组存在,则匹配问号和感叹号。 如果定义了 Open 组,则匹配会失败,这表示尖括号不平衡。 |
$ |
匹配输入字符串的末尾部分。 |
最终子表达式 (?(Open)(?!)),指示是否正确平衡输入单词符串中的嵌套构造(例如,是否每个左尖括号由右键括号匹配)。 使用基于有效的捕获组的条件匹配,有关详细信息请参阅备用构造。 如果定义了 Open 组,则正则表达式引擎会尝试匹配输入字符串中的子表达式 (?!)。 是不均衡的嵌套构造时,才应当定义 Open 组。 因此,要在输入字符串中匹配的模式应该是一个不能发生在输入字符串中的模式,其会导致匹配失败。
在此示例中,正则表达式引擎评估输入字符串“<abc><mno<xyz>>”,如下表所示。
步骤 |
模式 |
结果 |
---|---|---|
1 |
^ |
从输入字符串开头开始进行匹配 |
2 |
[^<>]* |
查找左尖括号之前的非尖括号字符;未找到匹配项。 |
3 |
(((?'Open'<) |
匹配“<abc>”中的左尖括号并将它分配给 Open 组。 |
4 |
[^<>]* |
与“abc”匹配。 |
5 |
)+ |
“<abc”是第二个捕获组的值。 输入字符串中的下一个字符不是左尖括号,因此正则表达式引擎不会循环回到 (?'Open'<)[^<>]*) 子模式。 |
6 |
((?'Close-Open'>) |
匹配“<abc>”中的右尖括号,将“abc”(Open 组和右尖括号之间的子字符串)分配给 Close 组并删除 Open 组的当前值(“<”)。 |
7 |
[^<>]* |
查找右尖括号之后的非尖括号字符;未找到匹配项。 |
8 |
)+ |
第三个捕获组的值是“>”。 输入字符串中的下一个字符不是右尖括号,因此正则表达式引擎不会循环回到 ((?'Close-Open'>)[^<>]*) 子模式。 |
9 |
)* |
第一个捕获组的值是“<abc>”。 输入字符串中的下一个字符是左尖括号,因此正则表达式引擎会循环回到 (((?'Open'<) 子模式。 |
10 |
(((?'Open'<) |
匹配“<mno>”中的左尖括号并将它分配给 Open 组。 其 Group.Captures 集合现在具有单个值“<”。 |
11 |
[^<>]* |
与“mno”匹配。 |
12 |
)+ |
“<mno”是第二个捕获组的值。 输入字符串中的下一个字符是左尖括号,因此正则表达式引擎会循环回到 (?'Open'<)[^<>]*) 子模式。 |
13 |
(((?'Open'<) |
匹配“<xyz>”中的左尖括号并将它分配给 Open 组。 Open 组的 Group.Captures 集合现在包括两个捕获:“<mno>”中的左尖括号和“<xyz>”中的左尖括号。 |
14 |
[^<>]* |
与“xyz”匹配。 |
15 |
)+ |
“<xyz”是第二个捕获组的值。 输入字符串中的下一个字符不是左尖括号,因此正则表达式引擎不会循环回到 (?'Open'<)[^<>]*) 子模式。 |
16 |
((?'Close-Open'>) |
匹配“<xyz>”中的右尖括号。 xyz",将 Open 组和右尖角符号之间的子字符串指定给 Close 组,并删除 Open 组的当前值。 前一个捕获的值(“<mno>”中的左尖括号)成为 Open 组的当前值。 Open 组的 Captures 集合现在包括单个捕获,即“<xyz>”中的左尖括号。 |
17 |
[^<>]* |
查找非尖括号字符;未找到匹配项。 |
18 |
)+ |
第三个捕获组的值是“>”。 输入字符串中的下一个字符是右尖括号,因此正则表达式引擎会循环回到 ((?'Close-Open'>)[^<>]*) 子模式。 |
19 |
((?'Close-Open'>) |
匹配“xyz>>”中的最后右尖括号,将“mno<xyz>”(Open 组和右尖括号之间的子字符串)分配给 Close 组并删除 Open 组的当前值。 Open 组现在为空。 |
20 |
[^<>]* |
查找非尖括号字符;未找到匹配项。 |
21 |
)+ |
第三个捕获组的值是“>”。 输入字符串中的下一个字符不是右尖括号,因此正则表达式引擎不会循环回到 ((?'Close-Open'>)[^<>]*) 子模式。 |
22 |
)* |
第一个捕获组的值是“<mno<xyz>>”。 输入字符串中的下一个字符不是左尖括号,因此正则表达式引擎不会循环回到 (((?'Open'<) 子模式。 |
23 |
(?(Open)(?!)) |
Open 组是未定义的,因此没有尝试匹配。 |
24 |
$ |
匹配输入字符串的末尾部分。 |
非捕获组
以下的分组构造不会捕获由子表达式匹配的子字符串:
(?:subexpression)
其中子表达式为任何有效正则表达式模式。 当一个限定符应用到一个组,但组捕获的子字符串并非所需时,通常会使用非捕获组构造。
注意 |
---|
如果正则表达式包含嵌套的分组构造,则外部非捕获组构造不适用于内部嵌套组构造。 |
下面的示例阐释包括非捕获组的正则表达式。 请注意输出不包含任何已捕获的组。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(?:\b(?:\w+)\W*)+\."
Dim input As String = "This is a short sentence."
Dim match As Match = Regex.Match(input, pattern)
Console.WriteLine("Match: {0}", match.Value)
For ctr As Integer = 1 To match.Groups.Count - 1
Console.WriteLine(" Group {0}: {1}", ctr, match.Groups(ctr).Value)
Next
End Sub
End Module
' The example displays the following output:
' Match: This is a short sentence.
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"(?:\b(?:\w+)\W*)+\.";
string input = "This is a short sentence.";
Match match = Regex.Match(input, pattern);
Console.WriteLine("Match: {0}", match.Value);
for (int ctr = 1; ctr < match.Groups.Count; ctr++)
Console.WriteLine(" Group {0}: {1}", ctr, match.Groups[ctr].Value);
}
}
// The example displays the following output:
// Match: This is a short sentence.
正则表达式 (?:\b(?:\w+)\W*)+\. 匹配由句号终止的语句。 因为正则表达式重点介绍句子,而不是个别单词,所以分组构造以独占方式用作限定符。 正则表达式模式的解释如下表所示。
模式 |
说明 |
---|---|
\b |
在单词边界处开始匹配。 |
(?:\w+) |
匹配一个或多个单词字符。 不将匹配的文本分配给捕获的组。 |
\W* |
匹配零个或多个非单词字符。 |
(?:\b(?:\w+)\W*)+ |
一次或多次匹配跟在零个或多个非单词字符后面以单词边界打头的一个或多个单词字符的模式。 不将匹配的文本分配给捕获的组。 |
\. |
匹配句点。 |
组选项
以下分组构造应用或禁用子表达式中指定的选项:
(?imnsx-imnsx: 子表达式 )
其中子表达式为任何有效正则表达式模式。 例如,(?i-s:) 将打开不区分大小写并禁用单行模式。 有关您可以指定的内联选项的更多信息,请参见正则表达式选项。
注意 |
---|
您可以指定将应用于整个正则表达式,而不是子表达式的选项,方法是使用 System.Text.RegularExpressions.Regex 类构造函数或静态方法。也可指定在正则表达式特定点后使用的内联选项,方法是使用 (?imnsx-imnsx) 语言构造。 |
组的选项构造并非捕获组。 即尽管子表达式捕获的字符串的任意部分包含在匹配中,但不会包含在捕获的组中也不会用于填充 GroupCollection 对象。
例如,以下示例中的正则表达式 \b(?ix: d \w+)\s 使用分组构造中的内联选项,以启用不区分大小写的匹配和在识别所有以字母 "d" 开头的单词时忽略模式空白。 该正则表达式的定义如下表所示。
模式 |
说明 |
---|---|
\b |
在单词边界处开始匹配。 |
(?ix: d \w+) |
使用不区分大小写的匹配并忽略此模式中的白色空间,匹配后跟一个或多个单词字符的 "d"。 |
\s |
与空白字符匹配。 |
Dim pattern As String = "\b(?ix: d \w+)\s"
Dim input As String = "Dogs are decidedly good pets."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("'{0}' found at index {1}.", match.Value, match.Index)
Next
' The example displays the following output:
' 'Dogs ' found at index 0.
' 'decidedly ' found at index 9.
string pattern = @"\b(?ix: d \w+)\s";
string input = "Dogs are decidedly good pets.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}// found at index {1}.", match.Value, match.Index);
// The example displays the following output:
// 'Dogs // found at index 0.
// 'decidedly // found at index 9.
零宽度正预测先行断言
以下的分组构造定义零宽度正预测先行断言:
(?= 子表达式 )
其中 subexpression 为任何正则表达式模式。 若要成功匹配,则输入字符串必须匹配子表达式中的正则表达式模式,尽管匹配的子字符串未包含在匹配结果中。 零宽度正预测先行断言不会回溯。
通常,零宽度正预测先行断言是在正则表达式模式的末尾找到的。 它定义了一个子字符串,该子字符串必须出现在匹配字符串的末尾但又不能包含在匹配结果中。 还有助于防止过度回溯。 可使用零宽度肯定性前瞻断言来确保特定捕获组以与专为该捕获组定义的模式的子集相匹配的文本开始。 例如,如果捕获组与连续单词字符相匹配,可以使用零宽度正预测先行断言要求第一个字符是按字母顺序排列的大写字符。
下面的示例使用零宽度正预测先行断言,以匹配输入字符串中谓词“is”前的单词。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b\w+(?=\sis\b)"
Dim inputs() As String = { "The dog is a Malamute.", _
"The island has beautiful birds.", _
"The pitch missed home plate.", _
"Sunday is a weekend day." }
For Each input As String In inputs
Dim match As Match = Regex.Match(input, pattern)
If match.Success Then
Console.WriteLine("'{0}' precedes 'is'.", match.Value)
Else
Console.WriteLine("'{0}' does not match the pattern.", input)
End If
Next
End Sub
End Module
' The example displays the following output:
' 'dog' precedes 'is'.
' 'The island has beautiful birds.' does not match the pattern.
' 'The pitch missed home plate.' does not match the pattern.
' 'Sunday' precedes 'is'.
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"\b\w+(?=\sis\b)";
string[] inputs = { "The dog is a Malamute.",
"The island has beautiful birds.",
"The pitch missed home plate.",
"Sunday is a weekend day." };
foreach (string input in inputs)
{
Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine("'{0}' precedes 'is'.", match.Value);
else
Console.WriteLine("'{0}' does not match the pattern.", input);
}
}
}
// The example displays the following output:
// 'dog' precedes 'is'.
// 'The island has beautiful birds.' does not match the pattern.
// 'The pitch missed home plate.' does not match the pattern.
// 'Sunday' precedes 'is'.
正则表达式模式 \b\w+(?=\sis\b) 可以解释为下表中所示内容。
模式 |
说明 |
---|---|
\b |
在单词边界处开始匹配。 |
\w+ |
匹配一个或多个单词字符。 |
(?=\sis\b) |
确定单词字符是否后接空白字符和字符串“是”,其在在单词边界处结束。 如果如此,则匹配成功。 |
零宽度负预测先行断言
以下的分组构造定义零宽度负预测先行断言:
(?! 子表达式 )
其中 subexpression 为任何正则表达式模式。 若要成功匹配,则输入字符串不得匹配子表达式中的正则表达式模式,尽管匹配的子字符串未包含在匹配结果中。
零宽度负预测先行断言通常用在正则表达式的开头或结尾。 正则表达式的开头可以定义当其定义了要被匹配的相似但更常规的模式时,不应被匹配的特定模式。 在这种情况下,它通常用于限制回溯。 正则表达式的末尾可以定义不能出现在匹配项末尾处的子表达式。
下面的示例定义了正则表达式匹配,其在正则表达式的开头使用零宽度预测先行断言,以匹配未以“un”开头的单词。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(?!un)\w+\b"
Dim input As String = "unite one unethical ethics use untie ultimate"
For Each match As Match In Regex.Matches(input, pattern, RegexOptions.IgnoreCase)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' one
' ethics
' use
' ultimate
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"\b(?!un)\w+\b";
string input = "unite one unethical ethics use untie ultimate";
foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// one
// ethics
// use
// ultimate
正则表达式模式 \b(?!un)\w+\b 可以解释为下表中所示内容。
模式 |
说明 |
---|---|
\b |
在单词边界处开始匹配。 |
(?!un) |
确定接下来的两个的字符是否为 "un"。 如果没有,则可能匹配。 |
\w+ |
匹配一个或多个单词字符。 |
\b |
在单词边界处结束匹配。 |
下面的示例定义了正则表达式匹配,其在正则表达式的末尾使用零宽度预测先行断言,以匹配未以标点字符结束的单词。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b\w+\b(?!\p{P})"
Dim input As String = "Disconnected, disjointed thoughts in a sentence fragment."
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' disjointed
' thoughts
' in
' a
' sentence
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"\b\w+\b(?!\p{P})";
string input = "Disconnected, disjointed thoughts in a sentence fragment.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// disjointed
// thoughts
// in
// a
// sentence
正则表达式模式 \b\w+\b(?!\p{P}) 的含义如下表所示。
模式 |
说明 |
---|---|
\b |
在单词边界处开始匹配。 |
\w+ |
匹配一个或多个单词字符。 |
\b |
在单词边界处结束匹配。 |
\p{P}) |
如果下个字符不是一个标点符号(如句点或逗号),则匹配成功。 |
零宽度正回顾后发断言
以下的分组构造定义零宽度正回顾先行断言:
(?<= 子表达式 )
其中 subexpression 为任何正则表达式模式。 若要成功匹配,则子表达式不得在输入字符串当前位置左侧出现,尽管 subexpression 未包含在匹配结果中。 零宽度正回顾后发断言不会回溯。
零宽度正预测先行断言通常在正则表达式的开头使用。 它们定义的模式是一个匹配的前提条件,但它不是匹配结果的一部分。
例如,下面的示例匹配二十一世纪年份的最后两个数字(也就是说,数字 "20" 要在匹配的字符串之前)。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "2010 1999 1861 2140 2009"
Dim pattern As String = "(?<=\b20)\d{2}\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' 10
' 09
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string input = "2010 1999 1861 2140 2009";
string pattern = @"(?<=\b20)\d{2}\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// 10
// 09
正则表达式模式 (?<=\b20)\d{2}\b 的含义如下表所示。
模式 |
说明 |
---|---|
\d{2} |
匹配两个十进制数字。 |
{?<=\b20) |
如果两个十进制数字的字边界以小数位数 "20" 开头,则继续匹配。 |
\b |
在单词边界处结束匹配。 |
零宽度肯定性回顾断言还用于在捕获组中的最后一个或多个字符不得为与该捕获组的正则表达式模式相匹配的字符的子集时限制回溯。 例如,如果组捕获所有的连续单词字符,可以使用零宽度正回顾断言要求最后一个字符时按字母顺序的。
零宽度负回顾后发断言
以下的分组构造定义零宽度负回顾后先行断言:
(?<! 子表达式 )
其中 subexpression 为任何正则表达式模式。 若要成功匹配,则子表达式不得在输入字符串当前位置的左侧出现。 但是,任何不匹配 subexpression 的子字符串不包含在匹配结果中。
零宽度负预测先行断言通常在正则表达式的开头使用。 它们定义的模式预先排除在后面的字符串中的匹配项。 它们还用于在捕获组中的最后一个或多个字符不得为与该捕获组的正则表达式模式相匹配的其中一个或多个字符时限制回溯。 例如,如果如果组捕获了所有的连续单词字符,可以使用零宽度正回顾断言要求最后一个字符不是下划线 (_)。
下面的示例匹配除周末之外的一周的任何一天(也就是星期六和星期日都没有)。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim dates() As String = { "Monday February 1, 2010", _
"Wednesday February 3, 2010", _
"Saturday February 6, 2010", _
"Sunday February 7, 2010", _
"Monday, February 8, 2010" }
Dim pattern As String = "(?<!(Saturday|Sunday) )\b\w+ \d{1,2}, \d{4}\b"
For Each dateValue As String In dates
Dim match As Match = Regex.Match(dateValue, pattern)
If match.Success Then
Console.WriteLine(match.Value)
End If
Next
End Sub
End Module
' The example displays the following output:
' February 1, 2010
' February 3, 2010
' February 8, 2010
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string[] dates = { "Monday February 1, 2010",
"Wednesday February 3, 2010",
"Saturday February 6, 2010",
"Sunday February 7, 2010",
"Monday, February 8, 2010" };
string pattern = @"(?<!(Saturday|Sunday) )\b\w+ \d{1,2}, \d{4}\b";
foreach (string dateValue in dates)
{
Match match = Regex.Match(dateValue, pattern);
if (match.Success)
Console.WriteLine(match.Value);
}
}
}
// The example displays the following output:
// February 1, 2010
// February 3, 2010
// February 8, 2010
正则表达式模式 (?<!(Saturday|Sunday) )\b\w+ \d{1,2}, \d{4}\b 的含义如下表所示。
模式 |
说明 |
---|---|
\b |
在单词边界处开始匹配。 |
\w+ |
匹配一个或多个后跟空白字符的单词字符。 |
\d{1,2}, |
匹配空白字符和逗号后面的一个或两个十进制数字。 |
\d{4}\b |
匹配四个十进制数字并在单词边界处结束匹配。 |
(?<!(Saturday|Sunday) ) |
如果匹配以字符串“星期六”或者“星期日”开头,后跟一个空格,则匹配成功。 |
非回溯子表达式
以下的分组构造表示非回溯子表达式(也称为一个"贪婪"子表达式):
(?> 子表达式 )
其中 subexpression 为任何正则表达式模式。
通常,如果正则表达式包含一个可选或可替代匹配模式并且备选不成功的话,正则表达式引擎可以在多个方向上分支以将输入的字符串与某种模式进行匹配。 如果未找到使用第一个分支的匹配项,则正则表达式引擎可以备份或回溯到使用第一个匹配项的点并尝试使用第二个分支的匹配项。 此过程可继续进行,直到尝试所有分支。
(?> 子表达式 )语言构造禁用回溯。 正则表达式引擎将在输入字符串中匹配尽可能多的字符。 可能不做进一步匹配时, 它将不回溯以尝试备用模式匹配。 (也就是说,该子表达式仅与可由该子表达式单独匹配的字符串匹配;子表达式不会尝试与基于该子表达式的字符串和任何该子表达式之后的子表达式匹配。
如果您知道回溯不会成功,则建议使用此选项。 防止正则表达式引擎执行不需要的搜索可以提高性能。
下面的示例阐释非回溯子表达式如何修改模式匹配的结果。 回溯正则表达式成功匹配一系列重复字符,在字边界上其后为相同字符,但非回溯的正则表达式不会匹配。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim inputs() As String = { "cccd.", "aaad", "aaaa" }
Dim back As String = "(\w)\1+.\b"
Dim noback As String = "(?>(\w)\1+).\b"
For Each input As String In inputs
Dim match1 As Match = Regex.Match(input, back)
Dim match2 As Match = Regex.Match(input, noback)
Console.WriteLine("{0}: ", input)
Console.Write(" Backtracking : ")
If match1.Success Then
Console.WriteLine(match1.Value)
Else
Console.WriteLine("No match")
End If
Console.Write(" Nonbacktracking: ")
If match2.Success Then
Console.WriteLine(match2.Value)
Else
Console.WriteLine("No match")
End If
Next
End Sub
End Module
' The example displays the following output:
' cccd.:
' Backtracking : cccd
' Nonbacktracking: cccd
' aaad:
' Backtracking : aaad
' Nonbacktracking: aaad
' aaaa:
' Backtracking : aaaa
' Nonbacktracking: No match
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string[] inputs = { "cccd.", "aaad", "aaaa" };
string back = @"(\w)\1+.\b";
string noback = @"(?>(\w)\1+).\b";
foreach (string input in inputs)
{
Match match1 = Regex.Match(input, back);
Match match2 = Regex.Match(input, noback);
Console.WriteLine("{0}: ", input);
Console.Write(" Backtracking : ");
if (match1.Success)
Console.WriteLine(match1.Value);
else
Console.WriteLine("No match");
Console.Write(" Nonbacktracking: ");
if (match2.Success)
Console.WriteLine(match2.Value);
else
Console.WriteLine("No match");
}
}
}
// The example displays the following output:
// cccd.:
// Backtracking : cccd
// Nonbacktracking: cccd
// aaad:
// Backtracking : aaad
// Nonbacktracking: aaad
// aaaa:
// Backtracking : aaaa
// Nonbacktracking: No match
nonbacktracking 正则表达式模式 (?>(\w)\1+).\b 的定义如下表所示。
模式 |
说明 |
---|---|
(\w) |
匹配单个字字符,并将其分配给第一捕获组。 |
\1+ |
一次或多次匹配的第一个捕获子字符串的值。 |
. |
匹配任意字符 |
\b |
在单词边界处结束匹配。 |
(?>(\w)\1+) |
匹配一个重复的单词字符的一个或多个匹配项,但不执行回溯以匹配在单词边界上的最后一个字符。 |
分组构造和正则表达式对象
由正则表达式捕获组匹配的子字符串由 System.Text.RegularExpressions.Group 对象表示,其从 System.Text.RegularExpressions.GroupCollection 对象检索,其由 Match.Groups 属性返回。 GroupCollection 对象被填充,如下所示:
集合中的第一个 Group 对象(位于索引零的对象)表示整个匹配。
下一组 Group 对象表示未命名(编号)的捕获组。 它们以在正则表达式中定义的顺序出现,从左至右。 这些组的索引值范围从 1 到集合中未命名捕获组的数目。 (特定组索引等效于其带编号的反向引用。 有关向后引用的更多信息,请参见反向引用构造。
最后的 Group 对象组表示命名的捕获组。 它们以在正则表达式中定义的顺序出现,从左至右。 第一个名为捕获组的索引值是一个大于最后一个未命名的捕获组的索引。 如果正则表达式中没有未命名捕获组,则第一个命名的捕获组的索引值为零。
如果将限定符应用于捕获组,则对应的 Group 对象的 Capture.Value、 Capture.Index 和 Capture.Length 属性反映捕获组捕获的最后一个子字符串。 可以检索一整组子字符串,其是按组捕获的并具有来自 CaptureCollection 对象的限定符,其由 Group.Captures 属性返回。
下面的示例阐释 Group 和 Capture 对象之间的关系。
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "(\b(\w+)\W+)+"
Dim input As String = "This is a short sentence."
Dim match As Match = Regex.Match(input, pattern)
Console.WriteLine("Match: {0}", match.Value)
For ctr As Integer = 1 To match.Groups.Count - 1
Console.WriteLine(" Group {0}: {1}", ctr, match.Groups(ctr).Value)
Dim capCtr As Integer = 0
For Each capture As Capture In match.Groups(ctr).Captures
Console.WriteLine(" Capture {0}: {1}", capCtr, capture.Value)
capCtr += 1
Next
Next
End Sub
End Module
' The example displays the following output:
' Match: This is a short sentence.
' Group 1: sentence.
' Capture 0: This
' Capture 1: is
' Capture 2: a
' Capture 3: short
' Capture 4: sentence.
' Group 2: sentence
' Capture 0: This
' Capture 1: is
' Capture 2: a
' Capture 3: short
' Capture 4: sentence
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"(\b(\w+)\W+)+";
string input = "This is a short sentence.";
Match match = Regex.Match(input, pattern);
Console.WriteLine("Match: {0}", match.Value);
for (int ctr = 1; ctr < match.Groups.Count; ctr++)
{
Console.WriteLine(" Group {0}: {1}", ctr, match.Groups[ctr].Value);
int capCtr = 0;
foreach (Capture capture in match.Groups[ctr].Captures)
{
Console.WriteLine(" Capture {0}: {1}", capCtr, capture.Value);
capCtr++;
}
}
}
}
// The example displays the following output:
// Match: This is a short sentence.
// Group 1: sentence.
// Capture 0: This
// Capture 1: is
// Capture 2: a
// Capture 3: short
// Capture 4: sentence.
// Group 2: sentence
// Capture 0: This
// Capture 1: is
// Capture 2: a
// Capture 3: short
// Capture 4: sentence
正则表达式模式 \b(\w+)\W+)+ 从字符串提取各个单词。 其定义如下表中所示:
模式 |
说明 |
---|---|
\b |
在单词边界处开始匹配。 |
(\w+) |
匹配一个或多个单词字符。 这些字符一起构成一个单词。 这是第二个捕获组。 |
\W+ |
匹配一个或多个非单词字符。 |
(\w+)\W+)+ |
一次或多次匹配跟在一个或多个非单词字符后面的一个或多个单词字符的模式。 这是第一个捕获组。 |
第一个捕获组匹配句子的每个单词。 第二个捕获组匹配每个单词,连同标点符号和该单词后的空白区域。 Group 对象的索引是 2,提供了有关由第二个捕获组匹配的文本的信息。 可从 CaptureCollection 对象获取捕获组捕获的整组字,该对象由 Group.Captures 属性返回。