正则表达式语言元素
概要
正则表达式提供了功能强大、灵活而又高效的方法来处理文本。正则表达式的全面模式匹配表示法使您可以快速分析大量文本以找到特定的字符模式;提取、编辑、替换或删除文本子字符串;或将提取的字符串添加到集合以生成报告。对于处理字符串(例如 HTML 处理、日志文件分析和 HTTP 标头分析)的许多应用程序而言,正则表达式是不可缺少的工具。
为操纵文本,对正则表达式语言进行了精心设计和优化。正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。元字符使正则表达式具有处理能力。
语言元素
字符转义
说明:大多数重要的正则表达式语言运算符都是非转义的单个字符。转义符 /(单个反斜杠)通知正则表达式分析器反斜杠后面的字符不是运算符。例如,分析器将星号 (*) 视为重复限定符,而将后跟星号的反斜杠 (/*) 视为 Unicode 字符 002A。下表中列出的字符转义在正则表达式和替换模式中都会被识别。
转义符 说明
一般字符 除 . $ ^ { [ ( | ) * + ? / 外,其他字符与自身匹配。
/a 与响铃(警报)/u0007 匹配。
/b 如果在 [] 字符类中,则与退格符 /u0008 匹配;如果不是这种情况,请参见本表后面的“注意”部分。
/t 与 Tab 符 /u0009 匹配。
/r 与回车符 /u000D 匹配。
/v 与垂直 Tab 符 /u000B 匹配。
/f 与换页符 /u000C 匹配。
/n 与换行符 /u000A 匹配。
/e 与 Esc 符 /u001B 匹配。
/040 将 ASCII 字符匹配为八进制数(最多三位);如果没有前导零的数字只有一位数或者与捕获组号相对应,则该数字为后向引用。(有关更多信息,请参见反向引用。) 例如,字符 /040 表示空格。
/x20 使用十六进制表示形式(恰好两位)与 ASCII 字符匹配。
/cC 与 ASCII 控制字符匹配;例如,/cC 为 Ctrl-C。
/u0020 使用十六进制表示形式(恰好四位)与 Unicode 字符匹配。
字符类
说明:字符类表示一组可以匹配输入字符串的字符。组合原义字符、转义符和字符类以构成正则表达式模式。
字符类 说明
[ 字符分组 ] (正字符分组。) 匹配指定字符分组内的任何字符。字符分组由串连的一个或多个原义字符、转义符、字符范围或字符类组成。
例如,若要指定所有元音字母,使用 [aeiou]. 若要指定所有标点符号和十进制数字符,使用代码 [/p{P}/d]。
[^ 字符分组 ] (负字符分组。) 匹配不在指定字符分组内的任何字符。字符分组由串连的一个或多个原义字符、转义符、字符范围或字符类组成。前导符 (^) 是强制的,指示字符分组为负字符分组,而不是正字符分组。
例如,若要指定除元音字母以外的所有字符,使用 [^aeiou]. 若要指定除标点符号和十进制数字符以外的所有字符,使用 [^/p{P}/d]。
[ 第一个字符 - 最后一个字符 ](字符范围。) 匹配字符范围中的任何字符。字符范围是一系列连续的字符,定义的方法是:指定系列中的第一个字符,连字符 (-),然后指定系列中的最后一个字符。如果两个字符具有相邻的 Unicode 码位,则这两个字符是连续的。可以串连两个或更多字符范围。
例如,若要指定从“0”至“9”的十进制数范围、从“a”至“f”的小写字母范围,以及从“A”至“F”的大写字母范围,使用 [0-9a-fA-F]。
.(句点字符。) 匹配除 /n 以外的任何字符。如果已用 Singleline 选项做过修改,则句点字符可与任何字符匹配。请注意,正字符分组或负字符分组中的句点字符(方括号内的句点)将被视为原义句点字符,而非字符类。
/p{ 名称 } 匹配通过名称(例如 Ll、Nd、Z、IsGreek 和 IsBoxDrawing)指定的 Unicode 通用类别或命名块中的任何字符。
/P{ 名称 } 匹配不在名称中指定的 Unicode 通用类别或命名块中的任何字符。
/w 与任何单词字符匹配。等效于 Unicode 通用类别 [/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}/p{Lm}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 /w 等效于 [a-zA-Z_0-9]。
/W 与任何非单词字符匹配。等效于 Unicode 通用类别 [^/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}/p{Lm}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 /W 等效于 [^a-zA-Z_0-9]。
/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 与任何十进制数字匹配。对于 Unicode 类别的 ECMAScript 行为,等效于 /p{Nd},对于非 Unicode 类别的 ECMAScript 行为,等效于 [0-9]。
/D 与任何非数字字符匹配。
默认情况下,如果匹配未成功,回溯会搜索其他可能的匹配。如果已知无法成功回溯,可以使用非回溯子表达式避免不必要的搜索,从而提高性能。
示例:
.* 匹配任意个字符。
/w* 匹配任意个字母或数字。
/s? 匹配0个或1个空格。
[1-9]/d{3}/-(0|1)?/d/-(0|1)/d 匹配 2008-08-08格式的时间
限定符
说明:限定符将可选数量的数据添加到正则表达式。限定符表达式应用于紧挨着它前面的字符、组或字符类
提供有关修改正则表达式的可选数量数据的信息。
限定符 说明
* 指定零个或更多个匹配;例如 /w* 或 (abc)*。等效于 {0,}。
+ 指定一个或多个匹配;例如 /w+ 或 (abc)+。等效于 {1,}。
? 指定零个或一个匹配;例如 /w? 或 (abc)?。等效于 {0,1}。
{ n } 指定恰好 n 个匹配;例如 (pizza){2}。
{ n ,} 指定至少 n 个匹配;例如 (abc){2,}。
{ n , m } 指定至少 n 个但不多于 m 个匹配。
*? 指定尽可能少地使用重复的第一个匹配(等效于 lazy *)。
+? 指定尽可能少地使用重复但至少使用一次(等效于 lazy +)。
?? 指定使用零次重复(如有可能)或一次重复 (lazy ?)。
{ n }? 等效于 {n} (lazy {n})。
{ n ,}? 指定尽可能少地使用重复但至少使用 n 次 (lazy {n,})。
{ n , m }? 指定介于 n 次和 m 次之间、尽可能少地使用重复 (lazy {n,m})。
原子零宽度断言
说明:下表中描述的元字符不会使引擎在字符串中前进或使用字符。它们只是根据字符串中的当前位置使匹配成功或失败。例如,^ 指定当前位置在行或字符串的开头。因此,正则表达式 ^FTP 只会返回那些在行的开头出现的字符串“FTP”的匹配项
断言 说明
^ 指定匹配必须出现在字符串的开头或行的开头。有关更多信息,请参见正则表达式选项中的 Multiline 选项。
$ 指定匹配必须出现在以下位置:字符串结尾、字符串结尾处的 /n 之前或行的结尾。有关更多信息,请参见正则表达式选项中的 Multiline 选项。
/A 指定匹配必须出现在字符串的开头(忽略 Multiline 选项)。
/Z 指定匹配必须出现在字符串的结尾或字符串结尾处的 /n 之前(忽略 Multiline 选项)。
/z 指定匹配必须出现在字符串的结尾(忽略 Multiline 选项)。
/G 指定匹配必须出现在上一个匹配结束的地方。与 Match.NextMatch() 一起使用时,此断言确保所有匹配都是连续的。
/b 指定匹配必须出现在 /w(字母数字)和 /W(非字母数字)字符之间的边界上。匹配必须出现在单词边界上(即出现在由任何非字母数字字符分隔的单词中第一个或最后一个字符上)。匹配也可以出现在字符串结尾处的单词边界上。
/B 指定匹配不得出现在 /b 边界上。
分组构造
说明:分组构造描述了正则表达式的子表达式,通常用于捕获输入字符串的子字符串。下表描述了正则表达式分组构造。
分组构造 说明
( 子表达式 ) 捕获匹配的子表达式(或非捕获组;有关更多信息,请参见正则表达式选项中的 ExplicitCapture 选项)。使用 () 的捕获基于左括号按顺序从 1 开始自动编号。捕获元素编号为零的第一个捕获是由整个正则表达式模式匹配的文本。
(?< name > 子表达式) 将匹配的子表达式捕获到一个组名称或编号名称中。用于 name 的字符串不得包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如 (?'name')。
(?< name1 - name2 > 子表达式)(平衡组定义。) 删除先前定义的 name2 组的定义,并在 name1 组中存储先前定义的 name2 组和当前组之间的间隔。如果未定义 name2 组,则匹配将回溯。由于删除 name2 的最后一个定义会显示 name2 的先前定义,因此该构造允许将 name2 组的捕获堆栈用作计数器,用于跟踪嵌套构造(如括号)。在此构造中,name1 是可选的。可以使用单引号替代尖括号,例如 (?'name1-name2')。
(?: 子表达式) (非捕获组。) 不捕获由子表达式匹配的子字符串。
(?imnsx-imnsx: 子表达式)应用或禁用子表达式中指定的选项。例如,(?i-s: ) 将打开不区分大小写并禁用单行模式。有关更多信息,请参见正则表达式选项。
(?= 子表达式) (零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。
(?! 子表达式) (零宽度负预测先行断言。) 仅当子表达式不在此位置的右侧匹配时才继续匹配。例如,/b(?!un)/w+/b 与不以 un 开头的单词匹配。
(?<= 子表达式) (零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
(?<! 子表达式) (零宽度负回顾后发断言。) 仅当子表达式不在此位置的左侧匹配时才继续匹配。
(?> 子表达式) (非回溯子表达式(也称为“贪婪”子表达式)。) 该子表达式仅完全匹配一次,然后就不会逐段参与回溯了。(也就是说,该子表达式仅与可由该子表达式单独匹配的字符串匹配。)
后向引用构造
说明:下表列出了用于将后向引用修饰符添加到正则表达式中的可选参数
后向引用构造 定义
/数字 后向引用。例如,(/w)/1 查找双写的单词字符。
/k<name> 命名后向引用。例如,(?<char>/w)/k<char> 查找双写的单词字符。表达式 (?<43>/w)/43 执行同样的操作。可以使用单引号替代尖括号,例如 /k'char'。
反向应用示例:(?<Name>/w+)/s*=/s*(?<char>["']?)(?<Value>.*)/k<char> 提取一个属性的名称和值。
替换
说明:只在替换模式中允许替换。对于正则表达式中的类似功能,使用后向引用(如 /1)。字符转义和替换是在替换模式中识别的唯一的特殊构造。下面几部分描述的所有语法构造只允许出现在正则表达式中;替换模式中不识别它们。例如,替换模式 a*${txt}b 会插入字符串“a*”,该字符串后跟按 txt 捕获组匹配的子字符串,该子字符串后跟字符串“b”(如果有)。在替换模式中,* 字符不会识别为元字符。与此类似,在正则表达式匹配模式中不识别 $ 模式。在正则表达式中,$ 指定字符串的结尾。
字符 说明
$ 数字 替换按组号 number(十进制)匹配的最后一个子字符串。
${ name } 替换由 (?<name> ) 组匹配的最后一个子字符串。
$$ 替换单个“$”字符。
$& 替换完全匹配本身的一个副本。
$` 替换匹配前的输入字符串的所有文本。
$' 替换匹配后的输入字符串的所有文本。
$+ 替换最后捕获的组。
$_ 替换整个输入字符串。
替换构造
说明:下表列出了用于修改正则表达式以允许进行二者之一/或匹配的特殊字符。
替换构造 定义
| 与以 |(竖线)字符分隔的术语中的任何一项匹配;例如, cat|dog|tiger。使用最左侧的成功匹配。
(?( 表达式 )yes|no) 如果表达式在此位置匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。表达式可以是任何有效的子表达式,但它将变为零宽度断言,因此该语法等效于 (?(?=expression)yes|no)。请注意,如果表达式是命名组的名称或捕获组编号,则替换构造将解释为捕获测试(在本表的下一行对此进行了描述)。若要避免在这些情况下产生混淆,则可以显式拼出内部 (?=expression)。
(?( name )yes|no) 如果命名捕获字符串有匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。如果给定的名称不与此表达式中使用的捕获组的名称或编号对应,则替换构造将解释为表达式测试(在本表的上一行进行了描述)。
其他构造
构造 定义
(? imnsx - imnsx ) 对诸如不区分大小写这样的选项进行设置或禁用以使其在模式中间打开或关闭。有关特定选项的信息,请参见正则表达式选项。在封闭组结束之前,选项更改将一直有效。请参见有关分组构造 (?imnsx-imnsx: ) 的信息,它是一个更为巧妙的形式。
(?# ) 插入到正则表达式内部的内联注释。该注释在第一个右括号字符处终止。
# [至行尾] X 模式注释。该注释以非转义的 # 开头,并继续到行的结尾。(请注意,必须激活 x 选项或 RegexOptions.IgnorePatternWhitespace 枚举选项才能识别此类注释。)
正则表达式选项
说明:可以使用影响匹配行为的选项修改正则表达式模式。可以通过下列两种基本方法之一设置正则表达式选项:可以在 Regex (pattern, options) 构造函数中的 options 参数中指定,其中 options 是 RegexOptions 枚举值的按位“或”组合;也可以使用内联 (?imnsx-imnsx:) 分组构造或 (?imnsx-imnsx) 其他构造在正则表达式模式内设置它们。
在内联选项构造中,一个选项或一组选项前面的减号 (-) 用于关闭这些选项。例如,内联构造 (?ix-ms) 将打开 IgnoreCase 和 IgnorePatternWhiteSpace 选项而关闭 Multiline 和 Singleline 选项。默认情况下,关闭所有正则表达式选项。
RegexOption 成员 内联字符 说明
None N/A 指定不设置任何选项。
IgnoreCase i 指定不区分大小写的匹配。
Multiline m 指定多行模式。更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。
ExplicitCapture n 指定唯一有效的捕获是显式命名或编号的 (?<name>…) 形式的组。这允许圆括号充当非捕获组,从而避免了由 (?:…) 导致的语法上的笨拙。
Compiled N/A 指定正则表达式将被编译为程序集。生成该正则表达式的 Microsoft 中间语言 (MSIL) 代码;以较长的启动时间为代价,得到更快的执行速度。
Singleline s 指定单行模式。更改句点字符 (.) 的含义,以使它与每个字符(而不是除 /n 之外的所有字符)匹配。
IgnorePatternWhitespace x 指定从模式中排除非转义空白并启用数字符号 (#) 后面的注释。(有关转义空白字符的列表,请参见字符转义。) 请注意,空白永远不会从字符类中消除。
RightToLeft N/A 指定搜索是从右向左而不是从左向右进行的。具有此选项的正则表达式将移动到起始位置的左边而不是右边。(因此,起始位置应指定为字符串的结尾而不是开头。)为了避免构造具有无限循环的正则表达式的可能性,此选项不能在中流指定。但是,(?<) 回顾后发构造提供了可用作子表达式的类似替代物。
RightToLeft 只更改搜索方向。它不会反转所搜索的子字符串。预测先行和回顾后发断言不改变:预测先行向右搜索;回顾后发向左搜索。
ECMAScript N/A 指定已为表达式启用了符合 ECMAScript 的行为。此选项仅可与 IgnoreCase 和 Multiline 标志一起使用。将 ECMAScript 同任何其他标志一起使用将导致异常。
CultureInvariant N/A 指定忽略语言中的区域性差异。有关更多信息,请参见在 RegularExpressions 命名空间中执行不区分区域性的操作。
正则表达的匹配行为
匹配行为
.NET Framework 正则表达式引擎是回溯的正则表达式匹配器,它并入了传统的非确定性有限自动机 (NFA) 引擎。这使其有别于更快的、但功能更有限的纯正则表达式确定性有限自动机 (DFA) 引擎,这也使其有别于标准化的、但较慢的 POSIX NFA。
三种正则表达式引擎类型
DFA 引擎: 在线性时状态下执行,因为它们不要求回溯(并因此它们永远不测试相同的字符两次)。DFA 引擎还可以确保匹配最长的可能的字符串。但是,因为 DFA 引擎只包含有限的状态,所以它不能匹配具有反向引用的模式;并且因为它不构造显示扩展,所以它不可以捕获子表达式。
传统的 NFA 引擎: 运行所谓的“贪婪的”匹配回溯算法,以指定顺序测试正则表达式的所有可能的扩展并接受第一个匹配项。因为传统的 NFA 构造正则表达式的特定扩展以获得成功的匹配,所以它可以捕获子表达式匹配和匹配的反向引用。但是,因为传统的 NFA 回溯,所以它可以访问完全相同的状态多次(如果通过不同的路径到达该状态)。因此,在最坏情况下,它的执行速度可能非常慢。因为传统的 NFA 接受它找到的第一个匹配,所以它还可能会导致其他(可能更长)匹配未被发现。
POSIX NFA 引擎: 与传统的 NFA 引擎类似,不同的一点在于:在它们可以确保已找到了可能的最长的匹配之前,它们将继续回溯。因此,POSIX NFA 引擎的速度慢于传统的 NFA 引擎;并且在使用 POSIX NFA 时,您恐怕不会愿意在更改回溯搜索的顺序的情况下来支持较短的匹配搜索,而非较长的匹配搜索。
程序员更为喜欢传统的 NFA 引擎的原因在于,NFA 引擎与 DFA 或 POSIX NFA 引擎相比更易于表达。尽管在最坏情况下 NFA 引擎的运行速度稍慢,但您可以通过使用降低多义性和限制回溯的模式,控制这些引擎以在线性时或多项式时状态下查找匹配。
.NET Framework 引擎功能
在充分利用传统 NFA 引擎优点的基础上,.NET Framework 正则表达式引擎包括了一组完整的构造,让程序员能够操纵回溯引擎。这些构造可被用于更快地找到匹配,或支持特定扩展,而非其他扩展。
其他功能包括:
“惰性”限定符:??、*?、+?、{n,m}?。这些惰性限定符指示回溯引擎首先搜索最少数目的重复。与之相反,普通的“贪婪的”限定符首先尝试匹配最大数目的重复。
积极的预测先行。这允许回溯引擎在匹配子表达式后返回到文本中相同的作用点。这对于通过验证起始于相同位置的多个模式来搜索整个文本是很有用的。
消极的预测先行。这增加了只在子表达式匹配失败的情况下才匹配表达式的能力。这对于删改一个搜索特别有用,因为与必须被包括在内的情况的表达式相比,应被排除的情况的表达式通常要简单得多。(例如,编写搜索不以“non”起始的单词的表达式就很困难)。
条件计算。这允许引擎可以根据以前的子表达式匹配的结果,使用多个替换模式进行搜索。这提供了超越反向引用所允许的、更为强大的功能,例如,当以前在子表达式中捕获了左括号时匹配右括号。
非回溯子表达式(也称作“贪婪”子表达式)。这允许回溯引擎确保子表达式只匹配为该子表达式找到的第一个匹配项,就好像该表达式独立于其包含的表达式运行。如果没有此构造,来自更大的表达式的回溯搜索可能会更改子表达式的行为。
从右到左匹配。这在从右到左而非从左到右搜索的情况下十分有用,或者在从模式的右侧部分开始搜索比从模式的左侧部分开始搜索更为有效的情况下十分有用。
积极的和消极的追溯。类似于预测先行。因为正则表达式引擎允许完全的从右到左匹配,所以正则表达式允许无限制的追溯。
反向引用
反向引用标识由正则表达式中的匹配组捕获的子字符串。每个反向引用都由一个编号或名称来标识,并通过“/编号”或“/k<名称>”表示法进行引用。 例如,如果输入字符串包含某任意子字符串的多个匹配项,则可以使用捕获组匹配第一个出现的子字符串,然后使用反向引用匹配后面出现的子字符串。
分析反向引用
表达式 /1 到 /9 总是指反向引用,而不是八进制代码。多位表达式 /10 和更高位表达式在具有与该数字对应的反向引用时被视作反向引用;否则,它们会被解释为八进制代码(除非起始位是 8 或 9,在这种情况下它们被视为原义的“8”和“9”)。如果正则表达式包含对未定义的组成员的反向引用,则它被视作分析错误。如果有多义性问题,可以使用 /k<n> 表示法,该表示法是明确的,并且不会与八进制符号代码混淆;同样,诸如 /xdd 等的十六进制代码也是明确的,并且不会与反向引用混淆。
匹配反向引用
反向引用引用组的最近的定义(当从左到右匹配时,最靠近左侧的定义)。具体地讲,就是当组建立多个捕获时,反向引用引用最近的捕获。
回溯
当正则表达式具有可选的或替换的匹配模式时,该正则表达式在其输入字符串的计算的某些点上,可以分支到一个或多个方向以完成所有可能的匹配。如果匹配在引擎搜索的第一个方向不成功,则它必须备份产生分支的输入字符串中的这一位置并尝试其他替换的匹配。
例如,假定设计一个正则表达式来匹配灰色一词的两种拼写形式:gray 和 grey。替换字符 | 用于创建正则表达式 gr(a|e)y,它可以与两种拼法中的任何一种匹配。当该正则表达式应用到输入字符串 greengraygrowngrey 时,假设引擎首先尝试匹配 gray。它与输入字符串中的前两个字符 gr 匹配,遇到 green 中的 e 后匹配失败。它回溯到 r(替换字符前的上一个成功匹配)并尝试匹配 grey。在第二个 e 上匹配失败,引擎继续搜索并将最终匹配两个嵌入的单词 gray 和 grey。
非回溯预测先行和追溯
积极的预测先行和追溯不回溯。也就是说,对其内容的处理方式与对非回溯 (?> ) 组的内容的处理方式相同。
因为预测先行和追溯始终是零宽度的,所以仅当捕获组出现在积极的预测先行和追溯中时,回溯行为才是可见的。例如,表达式 (?=(a*))/1a 永远找不到匹配,因为在预测先行内定义的组 1 占用了所有的字符“a”,而 /1a 又请求一个“a”。因为预测先行表达式不是回溯的,所以匹配引擎不会重试具有较少的“a”的组 1。
限定符和空匹配
限定符 *、+、{n,m}(及其“惰性”对等符号)永远不在空匹配后重复(在匹配了最小数目 n 后)。此规则避免限定符在 m 是无限时进入空匹配上的无限循环(即使 m 不是无限时,该规则也适用)。
例如,(a?)* 匹配字符串“aaa”并以模式 (a)(a)(a)() 捕获子字符串。注意,由于第四个空捕获使限定符停止重复,因此没有第五个空捕获。
同样,(a/1|(?(1)/1)){0,2} 与空字符串而不是“a”匹配,因为它从不尝试扩展 ()(a).。{0,2} 限定符只允许在最后一次迭代中有空匹配。与之不同的是,(a/1|(?(1)/1)){2} 实际与“a”匹配,原因是:它确实会尝试扩展 ()(a);最小迭代次数为 2,可强制引擎在空匹配后重复。
编译和重复使用
通过了解正则表达式引擎编译表达式的方式以及正则表达式的缓存方式,可以优化大量使用正则表达式的应用程序的性能。
已编译的正则表达式
默认情况下,正则表达式引擎将正则表达式编译成内部指令序列(这些指令序列是不同于 Microsoft 中间语言 (MSIL) 的高级代码)。当引擎执行正则表达式时,它解释该内部代码。
正则表达式缓存
为了提高性能,正则表达式引擎为已编译的正则表达式维护了一个应用程序范围的缓存。该缓存只存储静态方法调用中使用的正则表达式模式 (提供给实例方法的正则表达式模式将不被缓存)。这样在每次使用正则表达式时,就无需将正则表达式重新分析成高级字节代码。
C#中正则表达式相关类
Regex
Regex 类表示不可变(只读)的正则表达式。它还包含各种静态方法,允许在不显式创建其他类的实例的情况下使用其他正则表达式类。
Match
Match 类表示正则表达式匹配操作的结果。
MatchCollection
MatchCollection 类表示成功的非重叠匹配项的序列。该集合为不可变(只读)的,并且没有公共构造函数。MatchCollection 的实例是由 Regex.Matches 方法返回的。
Group
Group 类表示来自单个捕获组的结果。因为 Group 可以在单个匹配中捕获零个、一个或更多的字符串(使用限定符),所以它包含 Capture 对象的集合。因为 Group 继承自 Capture,所以可以直接访问最后捕获的子字符串(Group 实例本身等价于 Captures 属性返回的集合的最后一项)。
GroupCollection
GroupCollection 类表示被捕获的组的集合,并在单个匹配项中返回该捕获组的集合。该集合为不可变(只读)的,并且没有公共构造函数。GroupCollection 的实例在 Match.Groups 属性返回的集合中返回。
Capture
Capture 类包含来自单个子表达式捕获的结果。
CaptureCollection
CaptureCollection 类表示捕获的子字符串的序列,并返回由单个捕获组所执行的捕获集。由于限定符,捕获组可以在单个匹配中捕获多个字符串。Captures 属性(CaptureCollection 类的对象)作为 Match 和 Group 类的成员提供,目的是便于对捕获的子字符串的集合进行访问。
代码示例:
.cs
ASPX
示例
扫描 HREF
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);
}
}
更改日期格式
static 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}");
}
从 URL 中提取协议和端口号
String Extension(String url)
{
Regex r = new Regex(@"^(?<proto>/w+)://[^/]+?(?<port>:/d+)?/",
RegexOptions.Compiled);
return r.Match(url).Result("${proto}${port}");
}
从字符串中剥离无效字符
String CleanInput(string strIn)
{
// Replace invalid characters with empty strings.
return Regex.Replace(strIn, @"[^/w/.@-]", "");
}
验证字符串是否为有效的电子邮件格式
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})(/]?)$");
}
参考资源:
http://msdn.microsoft.com/zh-cn/library/hs600312.aspx
相关工具:
http://www.regexlab.com/zh/mtracer/
概要
正则表达式提供了功能强大、灵活而又高效的方法来处理文本。正则表达式的全面模式匹配表示法使您可以快速分析大量文本以找到特定的字符模式;提取、编辑、替换或删除文本子字符串;或将提取的字符串添加到集合以生成报告。对于处理字符串(例如 HTML 处理、日志文件分析和 HTTP 标头分析)的许多应用程序而言,正则表达式是不可缺少的工具。
为操纵文本,对正则表达式语言进行了精心设计和优化。正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。元字符使正则表达式具有处理能力。
语言元素
字符转义
说明:大多数重要的正则表达式语言运算符都是非转义的单个字符。转义符 /(单个反斜杠)通知正则表达式分析器反斜杠后面的字符不是运算符。例如,分析器将星号 (*) 视为重复限定符,而将后跟星号的反斜杠 (/*) 视为 Unicode 字符 002A。下表中列出的字符转义在正则表达式和替换模式中都会被识别。
转义符 说明
一般字符 除 . $ ^ { [ ( | ) * + ? / 外,其他字符与自身匹配。
/a 与响铃(警报)/u0007 匹配。
/b 如果在 [] 字符类中,则与退格符 /u0008 匹配;如果不是这种情况,请参见本表后面的“注意”部分。
/t 与 Tab 符 /u0009 匹配。
/r 与回车符 /u000D 匹配。
/v 与垂直 Tab 符 /u000B 匹配。
/f 与换页符 /u000C 匹配。
/n 与换行符 /u000A 匹配。
/e 与 Esc 符 /u001B 匹配。
/040 将 ASCII 字符匹配为八进制数(最多三位);如果没有前导零的数字只有一位数或者与捕获组号相对应,则该数字为后向引用。(有关更多信息,请参见反向引用。) 例如,字符 /040 表示空格。
/x20 使用十六进制表示形式(恰好两位)与 ASCII 字符匹配。
/cC 与 ASCII 控制字符匹配;例如,/cC 为 Ctrl-C。
/u0020 使用十六进制表示形式(恰好四位)与 Unicode 字符匹配。
字符类
说明:字符类表示一组可以匹配输入字符串的字符。组合原义字符、转义符和字符类以构成正则表达式模式。
字符类 说明
[ 字符分组 ] (正字符分组。) 匹配指定字符分组内的任何字符。字符分组由串连的一个或多个原义字符、转义符、字符范围或字符类组成。
例如,若要指定所有元音字母,使用 [aeiou]. 若要指定所有标点符号和十进制数字符,使用代码 [/p{P}/d]。
[^ 字符分组 ] (负字符分组。) 匹配不在指定字符分组内的任何字符。字符分组由串连的一个或多个原义字符、转义符、字符范围或字符类组成。前导符 (^) 是强制的,指示字符分组为负字符分组,而不是正字符分组。
例如,若要指定除元音字母以外的所有字符,使用 [^aeiou]. 若要指定除标点符号和十进制数字符以外的所有字符,使用 [^/p{P}/d]。
[ 第一个字符 - 最后一个字符 ](字符范围。) 匹配字符范围中的任何字符。字符范围是一系列连续的字符,定义的方法是:指定系列中的第一个字符,连字符 (-),然后指定系列中的最后一个字符。如果两个字符具有相邻的 Unicode 码位,则这两个字符是连续的。可以串连两个或更多字符范围。
例如,若要指定从“0”至“9”的十进制数范围、从“a”至“f”的小写字母范围,以及从“A”至“F”的大写字母范围,使用 [0-9a-fA-F]。
.(句点字符。) 匹配除 /n 以外的任何字符。如果已用 Singleline 选项做过修改,则句点字符可与任何字符匹配。请注意,正字符分组或负字符分组中的句点字符(方括号内的句点)将被视为原义句点字符,而非字符类。
/p{ 名称 } 匹配通过名称(例如 Ll、Nd、Z、IsGreek 和 IsBoxDrawing)指定的 Unicode 通用类别或命名块中的任何字符。
/P{ 名称 } 匹配不在名称中指定的 Unicode 通用类别或命名块中的任何字符。
/w 与任何单词字符匹配。等效于 Unicode 通用类别 [/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}/p{Lm}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 /w 等效于 [a-zA-Z_0-9]。
/W 与任何非单词字符匹配。等效于 Unicode 通用类别 [^/p{Ll}/p{Lu}/p{Lt}/p{Lo}/p{Nd}/p{Pc}/p{Lm}]。如果通过 ECMAScript 选项指定了符合 ECMAScript 的行为,则 /W 等效于 [^a-zA-Z_0-9]。
/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 与任何十进制数字匹配。对于 Unicode 类别的 ECMAScript 行为,等效于 /p{Nd},对于非 Unicode 类别的 ECMAScript 行为,等效于 [0-9]。
/D 与任何非数字字符匹配。
默认情况下,如果匹配未成功,回溯会搜索其他可能的匹配。如果已知无法成功回溯,可以使用非回溯子表达式避免不必要的搜索,从而提高性能。
示例:
.* 匹配任意个字符。
/w* 匹配任意个字母或数字。
/s? 匹配0个或1个空格。
[1-9]/d{3}/-(0|1)?/d/-(0|1)/d 匹配 2008-08-08格式的时间
限定符
说明:限定符将可选数量的数据添加到正则表达式。限定符表达式应用于紧挨着它前面的字符、组或字符类
提供有关修改正则表达式的可选数量数据的信息。
限定符 说明
* 指定零个或更多个匹配;例如 /w* 或 (abc)*。等效于 {0,}。
+ 指定一个或多个匹配;例如 /w+ 或 (abc)+。等效于 {1,}。
? 指定零个或一个匹配;例如 /w? 或 (abc)?。等效于 {0,1}。
{ n } 指定恰好 n 个匹配;例如 (pizza){2}。
{ n ,} 指定至少 n 个匹配;例如 (abc){2,}。
{ n , m } 指定至少 n 个但不多于 m 个匹配。
*? 指定尽可能少地使用重复的第一个匹配(等效于 lazy *)。
+? 指定尽可能少地使用重复但至少使用一次(等效于 lazy +)。
?? 指定使用零次重复(如有可能)或一次重复 (lazy ?)。
{ n }? 等效于 {n} (lazy {n})。
{ n ,}? 指定尽可能少地使用重复但至少使用 n 次 (lazy {n,})。
{ n , m }? 指定介于 n 次和 m 次之间、尽可能少地使用重复 (lazy {n,m})。
原子零宽度断言
说明:下表中描述的元字符不会使引擎在字符串中前进或使用字符。它们只是根据字符串中的当前位置使匹配成功或失败。例如,^ 指定当前位置在行或字符串的开头。因此,正则表达式 ^FTP 只会返回那些在行的开头出现的字符串“FTP”的匹配项
断言 说明
^ 指定匹配必须出现在字符串的开头或行的开头。有关更多信息,请参见正则表达式选项中的 Multiline 选项。
$ 指定匹配必须出现在以下位置:字符串结尾、字符串结尾处的 /n 之前或行的结尾。有关更多信息,请参见正则表达式选项中的 Multiline 选项。
/A 指定匹配必须出现在字符串的开头(忽略 Multiline 选项)。
/Z 指定匹配必须出现在字符串的结尾或字符串结尾处的 /n 之前(忽略 Multiline 选项)。
/z 指定匹配必须出现在字符串的结尾(忽略 Multiline 选项)。
/G 指定匹配必须出现在上一个匹配结束的地方。与 Match.NextMatch() 一起使用时,此断言确保所有匹配都是连续的。
/b 指定匹配必须出现在 /w(字母数字)和 /W(非字母数字)字符之间的边界上。匹配必须出现在单词边界上(即出现在由任何非字母数字字符分隔的单词中第一个或最后一个字符上)。匹配也可以出现在字符串结尾处的单词边界上。
/B 指定匹配不得出现在 /b 边界上。
分组构造
说明:分组构造描述了正则表达式的子表达式,通常用于捕获输入字符串的子字符串。下表描述了正则表达式分组构造。
分组构造 说明
( 子表达式 ) 捕获匹配的子表达式(或非捕获组;有关更多信息,请参见正则表达式选项中的 ExplicitCapture 选项)。使用 () 的捕获基于左括号按顺序从 1 开始自动编号。捕获元素编号为零的第一个捕获是由整个正则表达式模式匹配的文本。
(?< name > 子表达式) 将匹配的子表达式捕获到一个组名称或编号名称中。用于 name 的字符串不得包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如 (?'name')。
(?< name1 - name2 > 子表达式)(平衡组定义。) 删除先前定义的 name2 组的定义,并在 name1 组中存储先前定义的 name2 组和当前组之间的间隔。如果未定义 name2 组,则匹配将回溯。由于删除 name2 的最后一个定义会显示 name2 的先前定义,因此该构造允许将 name2 组的捕获堆栈用作计数器,用于跟踪嵌套构造(如括号)。在此构造中,name1 是可选的。可以使用单引号替代尖括号,例如 (?'name1-name2')。
(?: 子表达式) (非捕获组。) 不捕获由子表达式匹配的子字符串。
(?imnsx-imnsx: 子表达式)应用或禁用子表达式中指定的选项。例如,(?i-s: ) 将打开不区分大小写并禁用单行模式。有关更多信息,请参见正则表达式选项。
(?= 子表达式) (零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,/w+(?=/d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。
(?! 子表达式) (零宽度负预测先行断言。) 仅当子表达式不在此位置的右侧匹配时才继续匹配。例如,/b(?!un)/w+/b 与不以 un 开头的单词匹配。
(?<= 子表达式) (零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
(?<! 子表达式) (零宽度负回顾后发断言。) 仅当子表达式不在此位置的左侧匹配时才继续匹配。
(?> 子表达式) (非回溯子表达式(也称为“贪婪”子表达式)。) 该子表达式仅完全匹配一次,然后就不会逐段参与回溯了。(也就是说,该子表达式仅与可由该子表达式单独匹配的字符串匹配。)
后向引用构造
说明:下表列出了用于将后向引用修饰符添加到正则表达式中的可选参数
后向引用构造 定义
/数字 后向引用。例如,(/w)/1 查找双写的单词字符。
/k<name> 命名后向引用。例如,(?<char>/w)/k<char> 查找双写的单词字符。表达式 (?<43>/w)/43 执行同样的操作。可以使用单引号替代尖括号,例如 /k'char'。
反向应用示例:(?<Name>/w+)/s*=/s*(?<char>["']?)(?<Value>.*)/k<char> 提取一个属性的名称和值。
替换
说明:只在替换模式中允许替换。对于正则表达式中的类似功能,使用后向引用(如 /1)。字符转义和替换是在替换模式中识别的唯一的特殊构造。下面几部分描述的所有语法构造只允许出现在正则表达式中;替换模式中不识别它们。例如,替换模式 a*${txt}b 会插入字符串“a*”,该字符串后跟按 txt 捕获组匹配的子字符串,该子字符串后跟字符串“b”(如果有)。在替换模式中,* 字符不会识别为元字符。与此类似,在正则表达式匹配模式中不识别 $ 模式。在正则表达式中,$ 指定字符串的结尾。
字符 说明
$ 数字 替换按组号 number(十进制)匹配的最后一个子字符串。
${ name } 替换由 (?<name> ) 组匹配的最后一个子字符串。
$$ 替换单个“$”字符。
$& 替换完全匹配本身的一个副本。
$` 替换匹配前的输入字符串的所有文本。
$' 替换匹配后的输入字符串的所有文本。
$+ 替换最后捕获的组。
$_ 替换整个输入字符串。
替换构造
说明:下表列出了用于修改正则表达式以允许进行二者之一/或匹配的特殊字符。
替换构造 定义
| 与以 |(竖线)字符分隔的术语中的任何一项匹配;例如, cat|dog|tiger。使用最左侧的成功匹配。
(?( 表达式 )yes|no) 如果表达式在此位置匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。表达式可以是任何有效的子表达式,但它将变为零宽度断言,因此该语法等效于 (?(?=expression)yes|no)。请注意,如果表达式是命名组的名称或捕获组编号,则替换构造将解释为捕获测试(在本表的下一行对此进行了描述)。若要避免在这些情况下产生混淆,则可以显式拼出内部 (?=expression)。
(?( name )yes|no) 如果命名捕获字符串有匹配,则与“yes”部分匹配;否则,与“no”部分匹配。“no”部分可省略。如果给定的名称不与此表达式中使用的捕获组的名称或编号对应,则替换构造将解释为表达式测试(在本表的上一行进行了描述)。
其他构造
构造 定义
(? imnsx - imnsx ) 对诸如不区分大小写这样的选项进行设置或禁用以使其在模式中间打开或关闭。有关特定选项的信息,请参见正则表达式选项。在封闭组结束之前,选项更改将一直有效。请参见有关分组构造 (?imnsx-imnsx: ) 的信息,它是一个更为巧妙的形式。
(?# ) 插入到正则表达式内部的内联注释。该注释在第一个右括号字符处终止。
# [至行尾] X 模式注释。该注释以非转义的 # 开头,并继续到行的结尾。(请注意,必须激活 x 选项或 RegexOptions.IgnorePatternWhitespace 枚举选项才能识别此类注释。)
正则表达式选项
说明:可以使用影响匹配行为的选项修改正则表达式模式。可以通过下列两种基本方法之一设置正则表达式选项:可以在 Regex (pattern, options) 构造函数中的 options 参数中指定,其中 options 是 RegexOptions 枚举值的按位“或”组合;也可以使用内联 (?imnsx-imnsx:) 分组构造或 (?imnsx-imnsx) 其他构造在正则表达式模式内设置它们。
在内联选项构造中,一个选项或一组选项前面的减号 (-) 用于关闭这些选项。例如,内联构造 (?ix-ms) 将打开 IgnoreCase 和 IgnorePatternWhiteSpace 选项而关闭 Multiline 和 Singleline 选项。默认情况下,关闭所有正则表达式选项。
RegexOption 成员 内联字符 说明
None N/A 指定不设置任何选项。
IgnoreCase i 指定不区分大小写的匹配。
Multiline m 指定多行模式。更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。
ExplicitCapture n 指定唯一有效的捕获是显式命名或编号的 (?<name>…) 形式的组。这允许圆括号充当非捕获组,从而避免了由 (?:…) 导致的语法上的笨拙。
Compiled N/A 指定正则表达式将被编译为程序集。生成该正则表达式的 Microsoft 中间语言 (MSIL) 代码;以较长的启动时间为代价,得到更快的执行速度。
Singleline s 指定单行模式。更改句点字符 (.) 的含义,以使它与每个字符(而不是除 /n 之外的所有字符)匹配。
IgnorePatternWhitespace x 指定从模式中排除非转义空白并启用数字符号 (#) 后面的注释。(有关转义空白字符的列表,请参见字符转义。) 请注意,空白永远不会从字符类中消除。
RightToLeft N/A 指定搜索是从右向左而不是从左向右进行的。具有此选项的正则表达式将移动到起始位置的左边而不是右边。(因此,起始位置应指定为字符串的结尾而不是开头。)为了避免构造具有无限循环的正则表达式的可能性,此选项不能在中流指定。但是,(?<) 回顾后发构造提供了可用作子表达式的类似替代物。
RightToLeft 只更改搜索方向。它不会反转所搜索的子字符串。预测先行和回顾后发断言不改变:预测先行向右搜索;回顾后发向左搜索。
ECMAScript N/A 指定已为表达式启用了符合 ECMAScript 的行为。此选项仅可与 IgnoreCase 和 Multiline 标志一起使用。将 ECMAScript 同任何其他标志一起使用将导致异常。
CultureInvariant N/A 指定忽略语言中的区域性差异。有关更多信息,请参见在 RegularExpressions 命名空间中执行不区分区域性的操作。
正则表达的匹配行为
匹配行为
.NET Framework 正则表达式引擎是回溯的正则表达式匹配器,它并入了传统的非确定性有限自动机 (NFA) 引擎。这使其有别于更快的、但功能更有限的纯正则表达式确定性有限自动机 (DFA) 引擎,这也使其有别于标准化的、但较慢的 POSIX NFA。
三种正则表达式引擎类型
DFA 引擎: 在线性时状态下执行,因为它们不要求回溯(并因此它们永远不测试相同的字符两次)。DFA 引擎还可以确保匹配最长的可能的字符串。但是,因为 DFA 引擎只包含有限的状态,所以它不能匹配具有反向引用的模式;并且因为它不构造显示扩展,所以它不可以捕获子表达式。
传统的 NFA 引擎: 运行所谓的“贪婪的”匹配回溯算法,以指定顺序测试正则表达式的所有可能的扩展并接受第一个匹配项。因为传统的 NFA 构造正则表达式的特定扩展以获得成功的匹配,所以它可以捕获子表达式匹配和匹配的反向引用。但是,因为传统的 NFA 回溯,所以它可以访问完全相同的状态多次(如果通过不同的路径到达该状态)。因此,在最坏情况下,它的执行速度可能非常慢。因为传统的 NFA 接受它找到的第一个匹配,所以它还可能会导致其他(可能更长)匹配未被发现。
POSIX NFA 引擎: 与传统的 NFA 引擎类似,不同的一点在于:在它们可以确保已找到了可能的最长的匹配之前,它们将继续回溯。因此,POSIX NFA 引擎的速度慢于传统的 NFA 引擎;并且在使用 POSIX NFA 时,您恐怕不会愿意在更改回溯搜索的顺序的情况下来支持较短的匹配搜索,而非较长的匹配搜索。
程序员更为喜欢传统的 NFA 引擎的原因在于,NFA 引擎与 DFA 或 POSIX NFA 引擎相比更易于表达。尽管在最坏情况下 NFA 引擎的运行速度稍慢,但您可以通过使用降低多义性和限制回溯的模式,控制这些引擎以在线性时或多项式时状态下查找匹配。
.NET Framework 引擎功能
在充分利用传统 NFA 引擎优点的基础上,.NET Framework 正则表达式引擎包括了一组完整的构造,让程序员能够操纵回溯引擎。这些构造可被用于更快地找到匹配,或支持特定扩展,而非其他扩展。
其他功能包括:
“惰性”限定符:??、*?、+?、{n,m}?。这些惰性限定符指示回溯引擎首先搜索最少数目的重复。与之相反,普通的“贪婪的”限定符首先尝试匹配最大数目的重复。
积极的预测先行。这允许回溯引擎在匹配子表达式后返回到文本中相同的作用点。这对于通过验证起始于相同位置的多个模式来搜索整个文本是很有用的。
消极的预测先行。这增加了只在子表达式匹配失败的情况下才匹配表达式的能力。这对于删改一个搜索特别有用,因为与必须被包括在内的情况的表达式相比,应被排除的情况的表达式通常要简单得多。(例如,编写搜索不以“non”起始的单词的表达式就很困难)。
条件计算。这允许引擎可以根据以前的子表达式匹配的结果,使用多个替换模式进行搜索。这提供了超越反向引用所允许的、更为强大的功能,例如,当以前在子表达式中捕获了左括号时匹配右括号。
非回溯子表达式(也称作“贪婪”子表达式)。这允许回溯引擎确保子表达式只匹配为该子表达式找到的第一个匹配项,就好像该表达式独立于其包含的表达式运行。如果没有此构造,来自更大的表达式的回溯搜索可能会更改子表达式的行为。
从右到左匹配。这在从右到左而非从左到右搜索的情况下十分有用,或者在从模式的右侧部分开始搜索比从模式的左侧部分开始搜索更为有效的情况下十分有用。
积极的和消极的追溯。类似于预测先行。因为正则表达式引擎允许完全的从右到左匹配,所以正则表达式允许无限制的追溯。
反向引用
反向引用标识由正则表达式中的匹配组捕获的子字符串。每个反向引用都由一个编号或名称来标识,并通过“/编号”或“/k<名称>”表示法进行引用。 例如,如果输入字符串包含某任意子字符串的多个匹配项,则可以使用捕获组匹配第一个出现的子字符串,然后使用反向引用匹配后面出现的子字符串。
分析反向引用
表达式 /1 到 /9 总是指反向引用,而不是八进制代码。多位表达式 /10 和更高位表达式在具有与该数字对应的反向引用时被视作反向引用;否则,它们会被解释为八进制代码(除非起始位是 8 或 9,在这种情况下它们被视为原义的“8”和“9”)。如果正则表达式包含对未定义的组成员的反向引用,则它被视作分析错误。如果有多义性问题,可以使用 /k<n> 表示法,该表示法是明确的,并且不会与八进制符号代码混淆;同样,诸如 /xdd 等的十六进制代码也是明确的,并且不会与反向引用混淆。
匹配反向引用
反向引用引用组的最近的定义(当从左到右匹配时,最靠近左侧的定义)。具体地讲,就是当组建立多个捕获时,反向引用引用最近的捕获。
回溯
当正则表达式具有可选的或替换的匹配模式时,该正则表达式在其输入字符串的计算的某些点上,可以分支到一个或多个方向以完成所有可能的匹配。如果匹配在引擎搜索的第一个方向不成功,则它必须备份产生分支的输入字符串中的这一位置并尝试其他替换的匹配。
例如,假定设计一个正则表达式来匹配灰色一词的两种拼写形式:gray 和 grey。替换字符 | 用于创建正则表达式 gr(a|e)y,它可以与两种拼法中的任何一种匹配。当该正则表达式应用到输入字符串 greengraygrowngrey 时,假设引擎首先尝试匹配 gray。它与输入字符串中的前两个字符 gr 匹配,遇到 green 中的 e 后匹配失败。它回溯到 r(替换字符前的上一个成功匹配)并尝试匹配 grey。在第二个 e 上匹配失败,引擎继续搜索并将最终匹配两个嵌入的单词 gray 和 grey。
非回溯预测先行和追溯
积极的预测先行和追溯不回溯。也就是说,对其内容的处理方式与对非回溯 (?> ) 组的内容的处理方式相同。
因为预测先行和追溯始终是零宽度的,所以仅当捕获组出现在积极的预测先行和追溯中时,回溯行为才是可见的。例如,表达式 (?=(a*))/1a 永远找不到匹配,因为在预测先行内定义的组 1 占用了所有的字符“a”,而 /1a 又请求一个“a”。因为预测先行表达式不是回溯的,所以匹配引擎不会重试具有较少的“a”的组 1。
限定符和空匹配
限定符 *、+、{n,m}(及其“惰性”对等符号)永远不在空匹配后重复(在匹配了最小数目 n 后)。此规则避免限定符在 m 是无限时进入空匹配上的无限循环(即使 m 不是无限时,该规则也适用)。
例如,(a?)* 匹配字符串“aaa”并以模式 (a)(a)(a)() 捕获子字符串。注意,由于第四个空捕获使限定符停止重复,因此没有第五个空捕获。
同样,(a/1|(?(1)/1)){0,2} 与空字符串而不是“a”匹配,因为它从不尝试扩展 ()(a).。{0,2} 限定符只允许在最后一次迭代中有空匹配。与之不同的是,(a/1|(?(1)/1)){2} 实际与“a”匹配,原因是:它确实会尝试扩展 ()(a);最小迭代次数为 2,可强制引擎在空匹配后重复。
编译和重复使用
通过了解正则表达式引擎编译表达式的方式以及正则表达式的缓存方式,可以优化大量使用正则表达式的应用程序的性能。
已编译的正则表达式
默认情况下,正则表达式引擎将正则表达式编译成内部指令序列(这些指令序列是不同于 Microsoft 中间语言 (MSIL) 的高级代码)。当引擎执行正则表达式时,它解释该内部代码。
正则表达式缓存
为了提高性能,正则表达式引擎为已编译的正则表达式维护了一个应用程序范围的缓存。该缓存只存储静态方法调用中使用的正则表达式模式 (提供给实例方法的正则表达式模式将不被缓存)。这样在每次使用正则表达式时,就无需将正则表达式重新分析成高级字节代码。
C#中正则表达式相关类
Regex
Regex 类表示不可变(只读)的正则表达式。它还包含各种静态方法,允许在不显式创建其他类的实例的情况下使用其他正则表达式类。
Match
Match 类表示正则表达式匹配操作的结果。
MatchCollection
MatchCollection 类表示成功的非重叠匹配项的序列。该集合为不可变(只读)的,并且没有公共构造函数。MatchCollection 的实例是由 Regex.Matches 方法返回的。
Group
Group 类表示来自单个捕获组的结果。因为 Group 可以在单个匹配中捕获零个、一个或更多的字符串(使用限定符),所以它包含 Capture 对象的集合。因为 Group 继承自 Capture,所以可以直接访问最后捕获的子字符串(Group 实例本身等价于 Captures 属性返回的集合的最后一项)。
GroupCollection
GroupCollection 类表示被捕获的组的集合,并在单个匹配项中返回该捕获组的集合。该集合为不可变(只读)的,并且没有公共构造函数。GroupCollection 的实例在 Match.Groups 属性返回的集合中返回。
Capture
Capture 类包含来自单个子表达式捕获的结果。
CaptureCollection
CaptureCollection 类表示捕获的子字符串的序列,并返回由单个捕获组所执行的捕获集。由于限定符,捕获组可以在单个匹配中捕获多个字符串。Captures 属性(CaptureCollection 类的对象)作为 Match 和 Group 类的成员提供,目的是便于对捕获的子字符串的集合进行访问。
代码示例:
.cs
using
System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text.RegularExpressions;
using System.Text;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
mResultLabel.Text = Regex.IsMatch(mStringTextBox.Text, mExpressionTextBox.Text).ToString();
}
}
protected void mMarchButton_Click(object sender, EventArgs e)
{
MatchCollection matchCollection = Regex.Matches(mStringTextBox.Text, mExpressionTextBox.Text);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Match Count:" + matchCollection.Count);
int i = 0;
foreach (Match m in matchCollection)
{
stringBuilder.AppendLine(string.Format("Match{0}:{1}", i++, m.Value));
}
mResultTextBox.Text = stringBuilder.ToString();
}
protected void mCaptureButton_Click(object sender, EventArgs e)
{
MatchCollection matchCollection = Regex.Matches(mStringTextBox.Text, mExpressionTextBox.Text);
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
foreach (Match m in matchCollection)
{
stringBuilder.AppendLine(string.Format("Match{0}:{1}", i++, m.Value));
stringBuilder.AppendLine(string.Format(" Capture Count:{0}", m.Captures.Count));
for (int n = 0; n < m.Captures.Count;n++ )
{
stringBuilder.AppendLine(string.Format(" Capture{0}:{1}",n, m.Captures[n]));
}
}
mResultTextBox.Text = stringBuilder.ToString();
//Regex.capt(mStringTextBox.Text, mExpressionTextBox.Text);
}
protected void mGroupButton_Click(object sender, EventArgs e)
{
MatchCollection matchCollection = Regex.Matches(mStringTextBox.Text, mExpressionTextBox.Text);
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
foreach (Match m in matchCollection)
{
stringBuilder.AppendLine(string.Format("Match{0}:{1}", i++, m.Value));
stringBuilder.AppendLine(string.Format(" Groups Count:{0}", m.Groups.Count));
for (int n = 0; n < m.Groups.Count; n++)
{
stringBuilder.AppendLine(string.Format(" Groups{0}:{1}", n, m.Groups[n]));
}
}
mResultTextBox.Text = stringBuilder.ToString();
}
}
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text.RegularExpressions;
using System.Text;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
mResultLabel.Text = Regex.IsMatch(mStringTextBox.Text, mExpressionTextBox.Text).ToString();
}
}
protected void mMarchButton_Click(object sender, EventArgs e)
{
MatchCollection matchCollection = Regex.Matches(mStringTextBox.Text, mExpressionTextBox.Text);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Match Count:" + matchCollection.Count);
int i = 0;
foreach (Match m in matchCollection)
{
stringBuilder.AppendLine(string.Format("Match{0}:{1}", i++, m.Value));
}
mResultTextBox.Text = stringBuilder.ToString();
}
protected void mCaptureButton_Click(object sender, EventArgs e)
{
MatchCollection matchCollection = Regex.Matches(mStringTextBox.Text, mExpressionTextBox.Text);
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
foreach (Match m in matchCollection)
{
stringBuilder.AppendLine(string.Format("Match{0}:{1}", i++, m.Value));
stringBuilder.AppendLine(string.Format(" Capture Count:{0}", m.Captures.Count));
for (int n = 0; n < m.Captures.Count;n++ )
{
stringBuilder.AppendLine(string.Format(" Capture{0}:{1}",n, m.Captures[n]));
}
}
mResultTextBox.Text = stringBuilder.ToString();
//Regex.capt(mStringTextBox.Text, mExpressionTextBox.Text);
}
protected void mGroupButton_Click(object sender, EventArgs e)
{
MatchCollection matchCollection = Regex.Matches(mStringTextBox.Text, mExpressionTextBox.Text);
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
foreach (Match m in matchCollection)
{
stringBuilder.AppendLine(string.Format("Match{0}:{1}", i++, m.Value));
stringBuilder.AppendLine(string.Format(" Groups Count:{0}", m.Groups.Count));
for (int n = 0; n < m.Groups.Count; n++)
{
stringBuilder.AppendLine(string.Format(" Groups{0}:{1}", n, m.Groups[n]));
}
}
mResultTextBox.Text = stringBuilder.ToString();
}
}
ASPX
<%
@ Page Language
=
"
C#
"
AutoEventWireup
=
"
true
"
ValidateRequest
=
"
false
"
CodeFile
=
"
Default.aspx.cs
"
Inherits
=
"
_Default
"
%>
<! DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.1//EN " " http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd " >
< html xmlns = " http://www.w3.org/1999/xhtml " >
< head runat = " server " >
< title > C#中的正则表示类 </ title >
</ head >
< body >
< form id = " form1 " runat = " server " >
< div >
表达式: < asp:TextBox ID = " mExpressionTextBox " runat = " server " Width = " 625px " ></ asp:TextBox >
< br />
< br />
字符串: < asp:TextBox ID = " mStringTextBox " runat = " server " Width = " 625px " ></ asp:TextBox >
< br />
< br />
< asp:Button ID = " mMarchButton " runat = " server " Text = " March "
onclick = " mMarchButton_Click " />
& nbsp;
< asp:Button ID = " mCaptureButton " runat = " server " Text = " Capture "
onclick = " mCaptureButton_Click " />
& nbsp;
< asp:Button ID = " mGroupButton " runat = " server " Text = " Group "
onclick = " mGroupButton_Click " />
< br />
< br />
匹配结果: < asp:Label ID = " mResultLabel " runat = " server " Text = "" ></ asp:Label >
< br />
< br />
匹配内容:
< asp:TextBox ID = " mResultTextBox " TextMode = " MultiLine " Width = " 625px "
runat = " server " Height = " 150px " ></ asp:TextBox >
< br />
</ div >
</ form >
</ body >
</ html >
<! DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.1//EN " " http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd " >
< html xmlns = " http://www.w3.org/1999/xhtml " >
< head runat = " server " >
< title > C#中的正则表示类 </ title >
</ head >
< body >
< form id = " form1 " runat = " server " >
< div >
表达式: < asp:TextBox ID = " mExpressionTextBox " runat = " server " Width = " 625px " ></ asp:TextBox >
< br />
< br />
字符串: < asp:TextBox ID = " mStringTextBox " runat = " server " Width = " 625px " ></ asp:TextBox >
< br />
< br />
< asp:Button ID = " mMarchButton " runat = " server " Text = " March "
onclick = " mMarchButton_Click " />
& nbsp;
< asp:Button ID = " mCaptureButton " runat = " server " Text = " Capture "
onclick = " mCaptureButton_Click " />
& nbsp;
< asp:Button ID = " mGroupButton " runat = " server " Text = " Group "
onclick = " mGroupButton_Click " />
< br />
< br />
匹配结果: < asp:Label ID = " mResultLabel " runat = " server " Text = "" ></ asp:Label >
< br />
< br />
匹配内容:
< asp:TextBox ID = " mResultTextBox " TextMode = " MultiLine " Width = " 625px "
runat = " server " Height = " 150px " ></ asp:TextBox >
< br />
</ div >
</ form >
</ body >
</ html >
示例
扫描 HREF
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);
}
}
更改日期格式
static 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}");
}
从 URL 中提取协议和端口号
String Extension(String url)
{
Regex r = new Regex(@"^(?<proto>/w+)://[^/]+?(?<port>:/d+)?/",
RegexOptions.Compiled);
return r.Match(url).Result("${proto}${port}");
}
从字符串中剥离无效字符
String CleanInput(string strIn)
{
// Replace invalid characters with empty strings.
return Regex.Replace(strIn, @"[^/w/.@-]", "");
}
验证字符串是否为有效的电子邮件格式
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})(/]?)$");
}
参考资源:
http://msdn.microsoft.com/zh-cn/library/hs600312.aspx
相关工具:
http://www.regexlab.com/zh/mtracer/