文档 https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/regular-expressions
@TOC
-
用途
查找特定字符模式。
验证文本以确保它匹配预定义模式(如电子邮件地址)。
提取、编辑、替换或删除文本子字符串。 -
Regex 类
Regex.IsMatch 方法常用于验证字符串或确保字符串符合特定模式,而无需检索该字符串以供后续操作。
Regex.Match 方法返回一个 Match 对象,该对象包含有关与正则表达式模式匹配的第一个子字符串的信息。 如果 Match.Success 属性返回 true,则表示已找到一个匹配项,可以通过调用 Match.NextMatch 方法来检索有关后续匹配项的信息。 这些方法调用可以继续进行,直到 Match.Success 属性返回 false。
Regex.Matchs方法在输入字符串中搜索正则表达式的所有匹配项并返回所有匹配。foreach (Match match in Regex.Matches(input, pattern, RegexOptions.IgnoreCase)); //IgnoreCase不区分大小写
Regex.Replace方法在指定的输入字符串内,使用指定的替换字符串替换与某个正则表达式模式匹配的字符串。string result = Regex.Replace(input,pattern ,replacement,RegexOptions.IgnoreCase);
Regex.Split方法在由正则表达式匹配项定义的位置拆分输入字符串。(返回string[],匹配项起分割作用) -
匹配模式 https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/regular-expression-language-quick-reference
正则表达式引擎基于模式匹配,而不是比较和匹配文本。 模式由一个或多个字符文本、运算符或构造组成。
(建议写个界面便于学习)
string input = "输入字符串";
string pattern = "\\s+"; //表达式
string replacement = "X"; //用以替换,下面以X为例
string result = Regex.Replace(input, pattern, replacement);
转义类
\r
与回车符 \u000D 匹配。
\n
与换行符 \u000A 匹配。 pattern="\r\n(\w+)"
input=“\r\nThese are\ntwo lines.” → “X are\ntwo lines.”
字符类
ae
(与,连续,可以是任意字符,注意转义) " 00sfaej2a00e" replace(用X替换匹配项)→"00sfXj2a00e"
[]
(或,离散) [ae]
“00sfaej2a00e “→"00sfXXj2X00X”
[^]
(取反,须在[]内,否则表示字符串开头)[^aei]
“reign” 中的 “r”、“g” 和 “n” 。对aei
连续字符串取反,需分组构造。
[-]
(或,范围)[A-Z]
“AB123” 中的 “A” 和 “B”
[base_group-[excluded_group]]
减法(方括号 ([]) 和连字符 (-) 是强制的。)[0-9-[24]]
0-9除去2、4的数字
a.e
"nave” 中的 “ave” “water” 中的 “ate”
\s
空白字符。
\S
(非空)" 00s-a w-e"→"XXXXXX XXX"
\w
匹配一个单词(字母/数字/汉字…)字符。" 00s-aw-e" →"XXX-XX-X"
\d
与任何十进制数字匹配。
注:W,S,D,P等大写都是小写模式取反
\p{ name}\p{
与 name 指定的 Unicode 通用类别或命名块中的任何单个字符匹配。 [\p{P}]
匹配标点符号。
Unicode 通用类别和命名块链接
L 所有字母字符。 这包括 Lu大写、Ll、Lt、Lm 和 Lo 字符。
N 所有数字。 这包括 Nd、Nl 和 No 类别。
P 所有标点字符。 这包括 Pc、Pd、Ps, Pe、Pi、Pf 和 Po 类别。
S 所有符号。 这包括 Sm、Sc、Sk 和 So 类别。
Z 所有分隔符字符。 这包括 Zs、Zl 和 Zp 类别。
\p{IsGreek}+
匹配一个或多个希腊语字符。
0370 - 03FF IsGreek希腊字母
0600 - 06FF IsArabic
定位
^
字符串开头 ^\d{3}
“901-333-” 中的 “901”
$
字符串末尾 -\d{3}$
“-901-333” 中的 “-333”(前面的-901不会匹配到)
\A
须在字符串开头(不支持多行行首)
\Z
须在字符串末尾或末尾换行之前
\z
须在字符串末尾(不支持多行行首)
\G
须在上次匹配末尾,连续匹配\G\(\d\)
(拆解:\G \( \d \))"(1)(3)(5)[7](9)"
中的 “(1)”、“(3)” 和 “(5)”
\b...\b
在单词边界处开始 \b\w+\s\w+\b
多个词,空格,多个词,结束"them theme them them" 中的 “them theme” 和 “them them”(连续匹配是接着上一元素进行,所以没有theme them)
\B
不能在边界 \Bend\w*\b
"end sends endure lender" 中的 “ends” 和 “ender”
分组构造(捕获输入字符串的子字符串)
( subexpression)
(自动命名捕获组从1开始,通过match.Groups[index]访问)使用括号的捕获按正则表达式中左括号的顺序,从 1 开始,从左到右自动编号。 但是,命名的捕获组总是在非命名捕获组之后最后排序。 编号为零的捕获是与整个正则表达式模式匹配的文本。
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"(\w+)\s(\1)\W";
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+)
匹配一个或多个单词字符。 这是第一个捕获组 match.Groups[1]。→X X X X X X X X X.
\s
与空白字符匹配。→XXXXXXXXanswer.
(\1)
(访问捕获组1)与第一个捕获组捕获中的字符串匹配。 这是第二个捕获组match.Groups[2]。 该示例将其指定到捕获组上,以便可从 Match.Index 属性返回。→He said X was X correct answer.
\W
匹配包括空格和标点符号的一个非单词字符。 这样可以防止正则表达式模式匹配以第一个捕获组的单词开头的单词。→He said Xwas Xcorrect answer.
(?<name1-name2>subexpression)
平衡组。例如捕获成对的括号。(有点复杂)
(?<name>subexpression)
或(?'name'subexpression)
自定义命名。(?<duplicateWord>\w+)
匹配一个或多个单词字符。 命名此捕获组 duplicateWord。\k<duplicateWord>
(访问自定义命名的捕获组)匹配名为 duplicateWord的捕获的组。
(?:subexpression)
非捕获组。当一个限定符应用到一个组,但组捕获的子字符串并非所需时,通常会使用非捕获组构造。(只关注语句构造,并不需要捕获内容。)
(?imnsx-imnsx: subexpression)
组选项。选项值链接(i:不区分大小写;x:忽略空白 …)(?ix: d \w+)
使用不区分大小写的匹配并忽略此模式中的空白,匹配后跟一个或多个单词字符的“d”。
(?= subexpression)
零宽度正预测先行断言。\b\w+(?=\sis\b)
确定单词字符是否后接空白字符和字符串“is”,其在单词边界处结束。 “The dog is a Malamute.“→"The X is a Malamute.” 如果是\b\w+(\sis\b)
→"The X a Malamute.” ?=
定义了一个子字符串subexpression
,该子字符串必须出现在匹配字符串的末尾但又不能包含在匹配结果中,所以例子中is没有被replace。类似于+?
(?! subexpression)
零宽度负预测先行断言。\b(?!un)\w+\b
"unite one unethical ethics use untie ultimate"→"unite X unethical X X untie X"(前面不包含UN,且不捕获)
(?<= subexpression)
零宽度正回顾后发断言不会回溯。捕获subexpression
在左侧,且subexpression
不包含在匹配结果中。例(?<=\b20)\d{2}\b
"2010 1999 1861 2140 2009"→"20X 1999 1861 2140 20X"
(?<! subexpression)
捕获subexpression
不在左侧,且subexpression
不包含在匹配结果中。例(?<=\b20)\d{2}\b
"2010 1999 1861 2140 2009"→"2010 19X 18X 21X 2009"
(?> subexpression)
原子组(在其他一些正则表达式引擎中称为非回溯子表达式、原子子表达式或一次性子表达式)如果未找到使用第一个分支的匹配项,则正则表达式引擎可以备份或回溯到使用第一个匹配项的点并尝试使用第二个分支的匹配项。 此过程可继续进行,直到尝试所有分支。(多分支匹配,不是连续匹配,单支匹配完后回溯,下一支)
数量词
*
等效于{0,}
匹配上一个元素0次或多次。\w*
“00s-aw-e22¥2"→"XX-XX-XX¥XX”( 00S=X,X,-,aw=X,X,-,e22=X,X,¥,2=X,X)
+
匹配上一个元素1次或多次。"be+"
“been” 中的 “bee”(be基础上匹配+的上一个元素e)
?
匹配上一个元素0次或1次。(\s)?
“a b c d” →"XaXXbXXcXXXdX"
{n}
匹配上一个元素n次 ,\d{3}
“1,043.6” 中的 “,043”、“9,876,543,210” 中的 “,876”、“,543” 和 “,210”
{n,}
匹配上一个元素n-无限次
{n,m}
匹配上一个元素n-m次
*?
匹配上一个元素0次或多次但次数尽可能少。\w*?
“00s-aw-e22¥2"→"X0X0XsX-XaXwX-XeX2X2X¥X2X”
+?
匹配上一个元素1次或多次但次数尽可能少。"be+?"
“been” 中的 “be”(匹配到了但未选中,"be"
则并未进行下一位的匹配,有差异)
??
"be+匹配上一个元素0次或1次但次数尽可能少。"rai??"
“rain” 中的 “ra”
{n}?
匹配前面的元素恰好 n 次。",\d{3}?"
“1,043.6” 中的 “,043”
{n,}?
{n,m}?
反向引用(标识重复字符)
如果输入字符串包含某任意子字符串的多个匹配项,可以使用捕获组匹配第一个出现的子字符串,然后使用反向引用匹配后面出现的子字符串。
"(\w)\1"
“trellis llama webbing dresser swagger"→"trellis llama webbing dresser swagger”(逐字\w匹配,下一个w与前一个w匹配)
(?<name>\w)\k<name>
对命名为name(可以是char或数字)的捕获向后引用,例"(?<char>\w)\k<char>"
“trellis llama webbing dresser swagger"→"treXis Xama weXing dreXer swaXer”
(?<1>a)(?<1>\1b)*
当组建立多个捕获时,反向引用会引用最新的捕获。最左侧的定义是最新的定义,指定group名为1,第二组捕获命名为1,反向引用\1
的值赋给前者。"aababb"→
match.value=aababb
group[0].index= 0----group[0].value=aababb (group[0]是match.value,捕获从group[1]开始)
group[1].index= 3----group[1].value=abb
步骤1:从该字符串的开头开始,成功将“a”与表达式 (?<1>a) 匹配。 此时,1 组的值为“a”。
步骤2:继续匹配第二个字符,成功将字符串“ab”与表达式 \1b 或“ab”匹配。 然后,将结果“ab”分配到 \1。此时,1 组的值为“ab”
步骤3:继续匹配第四个字符。 表达式 (?<1>\1b)* 要匹配零次或多次,因此会成功将字符串“abb”与表达式 \1b 匹配。 然后,将结果“abb”分配回到 \1。此时,1 组的值为“abb”
替换构造
|
(或) th(e|is|at)
“this is the day.” 中的 “the” 和 “this”
(?( expression)yes|no)(?()
|条件匹配(类似于条件运算符 (?:)
)yes 是当 expression 匹配时要匹配的模式,而 no 是 expression 不匹配时要匹配的可选模式(如果未提供 no 模式,则其等效于一个空 no)。
\b(?(\d{2}-)\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b
的释义如下表所示:
\b 在单词边界处开始。
(?(\d{2}-) 确定接下来的三个字符是否由两个数字后接一个连字符组成。
\d{2}-\d{7} 如果前面的模式匹配,则匹配后接一个连字符和七个数字的两个数字。
\d{3}-\d{2}-\d{4} 如果前面的模式不匹配,则匹配三个十进制数字,后接一个连字符,再接两个十进制数字,再接另一个连字符,再接四个十进制数字。
\b 与字边界匹配。
01-9999999 020-333333 777-88-9999→X 020-333333 X
替代
$number
语言元素包括替换字符串中 number 捕获组所匹配的最后一个子字符串,其中 number 是捕获组的索引。 例如,替换模式 $1 指示匹配的子字符串将由捕获的第一个组替换。(Regex.Replace方法的replacement参数,如果是$格式)
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"\p{Sc}*(\s?\d+[.,]?\d*)\p{Sc}*";
string replacement = "$1";
string input = "$16.32 12.19 £16.29 €18.29 €18,29";
string result = Regex.Replace(input, pattern, replacement);
Console.WriteLine(result);
}
}
// The example displays the following output:
// 16.32 12.19 16.29 18.29 18,29
(\s?\d+[.,]?\d*)
匹配空白,后跟一个或多个十进制数,再后跟零个或一个句点或逗号,后跟零个或多个十进制数。 这是第一个捕获组。 因为替换模式为 $1,所以调用 Regex.Replace 方法会将整个匹配的子字符串替换为此捕获组。
4. Match Group Capture
Match 类表示单个正则表达式匹配项的结果。
Group 类表示捕获组匹配的子字符串,其从 System.Text.RegularExpressions.GroupCollection (集合)对象检索,其由 Match.Groups 属性返回。(分组(带括号)匹配的多个结果)
CaptureCollection 属性返回 Group.Captures 对象中获取由捕获组生成的整个捕获集。集合中的每个成员均为一个表示由该捕获组group[index]生成的捕获的 Capture 对象,这些对象按被捕获的顺序排列(因而也就是遵循在输入字符串中按从左至右匹配捕获的字符串的顺序)。
- Regex 类本身是线程安全且不可变的(只读)。 也就是说,可以在任何线程上创建 Regex
对象并在线程间共享;可以从任何线程调用匹配方法并且始终不会更改全局状态。
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(\S+)\s?
"This is the first sentence of the first paragraph. “→"XXXXXXXXparagraph”
\bth[^o]\w+\b
从词边界开始逐个词组(空白分隔)th开头,接着不是o,接着是1到多个词w。“thought thing though them through thus thorough this"→"thought X though X X X thorough X”
\b[.?!;:](\s|\z)
从词边界开始匹配任意(.*
)(除了\n)直到5标点之一,再到空白或是字符串末尾
^(\(?\d{3}\)?[\s-])?\d{3}-\d{4}$
^从输入字符串的开头部分开始,(?匹配零个或一个“(”文本字符,\d{3}匹配三个十进制数字,)?匹配零个或一个“)”文本字符,[\s-] 匹配连字符或空白字符,((?\d{3})?[\s-])? (匹配后跟三个十进制数字的可选左括号、可选右括号和空白字符或连字符零次或一次, 这是第一个捕获组)\d{3}-\d{4} 匹配后跟连字符和四个以上的十进制数字的三个十进制数字,$字符串末尾。
// 111 111-1111: matched
// 222-2222: matched
// 222 333-444: match failed
// (212) 111-1111: matched
// 111-AB1-1111: match failed
// 212-111-1111: matched
// 01 999-9999: match failed