c# 10 教程:25 正则表达式

admin 2025-07-23 159人围观 ,发现285个评论

正则表达式语言标识字符模式。支持正则表达式的.NET类型基于Perl5正则表达式,同时支持搜索和搜索/替换功能。

正则表达式用于以下任务:

验证文本输入,例如密码和电话号码

将文本数据分析为更结构化的形式(例如,NuGet版本字符串)

替换文档中的文本模式(例如,仅整个单词)

本章分为两个概念部分,介绍.NET中正则表达式的基础知识,以及介绍正则表达式的参考部分。

所有正则表达式类型都在中定义。

注意

本章中的示例都预加载到LINQPad中,其中还包括一个交互式正则表达式工具(按Ctrl+Shift+F1)。在线工具可在获得。

正则表达式基础知识

最常见的正则表达式运算符之一是。?是与前面的项目0或1时间匹配的量词。换句话说,?表示。项目可以是单个字符,也可以是方括号中的复杂字符结构。例如,正则表达式“colou?r”匹配颜色和,但不匹配colouur:

(("color",@"colou?r").Success);//(("colour",@"colou?r").Success);//(("colouur",@"colou?r").Success);//False

在较大的字符串中进行搜索。它返回的对象具有匹配项的索引和长度以及匹配的实际值的属性:

Matchm=("anycolouryoulike",@"colou?r");();//();//4();//6();//(());//colour

你可以把看作是字符串的IndexOf方法的一个更强大的版本。不同之处在于它搜索而不是文字字符串。

方法是一个快捷方式,用于调用Match,然后测试Success属性。

默认情况下,正则表达式引擎从左到右工作,因此仅返回最左侧的匹配项。可以使用NextMatch方法返回更多匹配项:

Matchm1=("Onecolor?Therearetwocoloursinmyhead!",@"colou?rs?");Matchm2=();(m1);//(m2);//colours

方法返回数组中的所有匹配项。我们可以重写前面的示例,如下所示:

foreach(("Onecolor?Therearetwocoloursinmyhead!",@"colou?rs?"))(m);

另一个常见的正则表达式运算符是,用竖线|表示。交流发电机表示替代方案。以下匹配“Jen”、“Jenny”和“Jennifer”:

(("Jenny","Jen(ny|nifer)?"));//True

交流发电机周围的括号将备选方案与表达式的其余部分分开。

注意

您可以在匹配正则表达式时指定超时。如果匹配操作花费的时间超过指定的TimeSpan,则会抛出RegexMatchTimeoutException。如果程序处理任意正则表达式(例如,在高级搜索对话框中),这会很有用,因为它可以防止格式错误的正则表达式无限旋转。

编译的正则表达式

在前面的一些示例中,我们使用相同的模式重复调用静态RegEx方法。在这些情况下,另一种方法是使用模式和实例化Regex对象,然后调用实例:

Regexr=newRegex(@"sausages?",);(("sausage"));//(("sausages"));//sausages

指示RegEx实例使用轻量级代码生成(中的DynamicMethod)来动态构建和编译针对该特定正则表达式定制的代码。这样可以加快匹配速度,但会降低初始编译成本。

您还可以实例化Regex对象,而无需使用。正则表达式实例是不可变的。

注意

正则表达式引擎速度很快。即使没有编译,简单的匹配通常也不到一微秒。

正则表达式选项

正则表达式选项标志枚举允许您调整匹配行为。RegexOptions的常见用途是执行不区分大小写的搜索:

(("a","A",));//a

这将应用当前区域性的大小写等效规则。使用“区域性不变”标志可以请求固定区域性,改为:

(("a","A",|));

您可以使用单字母代码激活正则表达式本身中的大多数RegexOptions标志,如下所示:

(("a",@"(?i)A"));//a

您可以在整个表达式中打开和关闭选项:

(("AAAa",@"(?i)a(?-i)a"));//Aa

另一个有用的选项是IgnorePatternWhitespace或(?x)。这允许您插入空格以使正则表达式更具可读性,而无需按字面意思获取空格。

列出了所有RegExOptions值及其单字母代码。

正则表达式选项

枚举值

正则表达式代码

描述

没有

忽略案例

忽略大小写(默认情况下,正则表达式区分大小写)

多行

m

更改^和$,以便它们匹配行的开头/结尾,而不是字符串的开头/结尾

显式捕获

n

仅捕获显式命名或显式编号的组(请参阅)

编译

强制编译到IL(请参阅)

单线

s

使。匹配每个字符(而不是匹配除\n之外的每个字符)

忽略模式空白

x

从模式中消除未转义的空格

从右到左

r

从右到左搜索;无法在中游指定

ECMAScript

强制符合ECMA标准(默认情况下,实现不符合ECMA标准)

文化不变

关闭字符串比较的区域性特定行为

角色转义

正则表达式具有以下元字符,这些元字符具有特殊含义而不是字面含义:

\*+?|{[()^$.@文本表示。这是为了绕过C34;));//F

当您指定或在表达式中包含(?m)时:

^匹配字符串或的开头(紧跟在\n之后)。

$匹配字符串或的末尾(紧邻\n之前)。

在多行模式下使用$有一个问题:Windows中的新行几乎总是用\r\n表示,而不仅仅是\n。这意味着要使$对Windows文件有用,您通常还必须匹配\r,并具有:

(?=\r?$)

可确保\r不会成为结果的一部分。以下匹配以“.txt”结尾的行:

stringfileNames=""+"\r\n"+""+"\r\n"+"";stringr=@".+\.txt(?=\r?$)";foreach((fileNames,r,))(m+"");OUTPUT:

以下内容匹配字符串s中的所有空行:

MatchCollectionemptyLines=(s,"^(?=\r?$)",);

以下内容匹配所有空行或仅包含空格的行:

MatchCollectionblankLines=(s,"^[\t]*(?=\r?$)",);
注意

由于锚点匹配位置而不是字符,因此指定锚点本身与空字符串匹配:

(("x","代码:
(("5islessthan10",@"\d+",m=(()*10).ToString()));OUTPUT:50islessthan100

在中,我们展示了如何使用MatchEvaluator为HTML适当地转义Unicode字符。

拆分文本

静态方法是字符串的更强大的版本。拆分方法,正则表达式表示分隔符模式。在这个例子中,我们拆分一个字符串,其中任何数字都算作分隔符:

foreach(("a5b7c",@"\d"))(s+"");//abc

此处的结果不包括分隔符本身。但是,您可以通过将表达式包装在中来包含分隔符。下面将驼峰大小写字符串拆分为单独的单词:

foreach(("oneTwoThree",@"(?=[A-Z])"))(s+"");//oneTwoThree
说明书正则表达式食谱

匹配美国社会安全号码/电话号码stringssNum=@"\d{3}-\d{2}-\d{4}";(("123-45-6789",ssNum));//Truestringphone=@"(?x)(\d{3}[-\s]|\(\d{3}\)\s?)\d{3}[-\s]?\d{4}";(("",phone));//(("(123)456-7890",phone));//True

提取“名称=值”对(每行一个)

请注意,这以指令(?m)开头:

stringr=@"(?m)^\s*(?'name'\w+)\s*=\s*(?'value'.*)\s*(?=\r?$)";stringtext=@"id=3secure=truetimeout=30";foreach((text,r))(["name"]+"is"+["value"]);idis3secureistruetimeoutis30
强密码验证

下面检查密码是否至少包含六个字符,以及密码是否包含数字、符号或标点符号:

stringr=@"(?x)^(?=.*(\d|\p{P}|\p{S})).{6,}";(("abc12",r));//(("abcdef",r));//(("ab88yz",r));//True

至少80个字符的行stringr=@"(?m)^.{80,}(?=\r?$)";stringfifty=newstring('x',50);stringeighty=newstring('x',80);stringtext=eighty+"\r\n"+fifty+"\r\n"+eighty;((text,r).Count);//2

解析日期/时间(N/N/NH:M:SAM/PM)

此表达式处理各种数字日期格式,无论年份排在第一位还是最后一年,该表达式都有效。(?x)指令通过允许空格来提高可读性;(?i)关闭区分大小写(对于可选的AM/PM指示符)。然后,您可以通过组集合访问比赛的每个组件:

stringr=@"(?x)(?i)(\d{1,4})[./-](\d{1,2})[./-](\d{1,4})[\sT](\d+):(\d+):(\d+)\s?(A\.?M\.?|P\.?M\.?)?";stringtext="01/02/20085:20:50PM";foreach((text,r).Groups)(+"");01/02/20085:20:50PM0102200852050PM

(当然,这不会验证日期/时间是否正确。

匹配的罗马数字stringr=@"(?i)\bm*"+@"(d?c{0,3}|c[dm])"+@"(l?x{0,3}|x[lc])"+@"(v?i{0,3}|i[vx])"+@"\b";(("MCMLXXXIV",r));//True

删除重复的单词

在这里,我们捕获一个名为dupe的命名组:

stringr=@"(?'dupe'\w+)\W\k'dupe'";stringtext="Inthethebeginning";((text,r,"${dupe}"));Inthebeginning

字数stringr=@"\b(\w|[-'])+\b";stringtext="It'sallmumbo-jumbotome";((text,r).Count);//5

匹配GUIDstringr=@"(?i)\b"+@"[0-9a-fA-F]{8}\-"+@"[0-9a-fA-F]{4}\-"+@"[0-9a-fA-F]{4}\-"+@"[0-9a-fA-F]{4}\-"+@"[0-9a-fA-F]{12}"+@"\b";stringtext="Itskeyis{3F2504E0-4F89-11D3-9A0C-0305E82C3301}.";((text,r).Index);//12

解析XML/HTML标记

正则表达式对于解析HTML片段很有用,尤其是当文档的格式可能不完美时:

stringr=@"(?'tag'\w+?).*"+//lazy-matchfirsttag,andnameit'tag'@"(?'text'.*?)"+//lazy-matchtextcontent,nameit'textd'@"/\k'tag'";//matchlasttag,denotedby'tag'stringtext="h1hello/h1";Matchm=(text,r);(["tag"]);//(["text"]);//hello
拆分驼峰大小写的单词

这需要以包含大写分隔符:

stringr=@"(?=[A-Z])";foreach(("oneTwoThree",r))(s+"");//oneTwoThree

获取合法文件名stringinput="My\"good\"";char[]invalidChars=();stringinvalidString=(newstring(invalidChars));stringvalid=(input,"["+invalidString+"]","");(valid);

转义HTML的Unicode字符stringhtmlFragment="©2007";stringresult=(htmlFragment,@"[\u0080-\uFFFF]",m=@""+((int)[0]).ToString()+";");(result);//©2007

HTTP查询字符串中的取消转义字符stringsample="C%23rocks";stringresult=(sample,@"%[0-9a-f][0-9a-f]",m=((char)((1),16)).ToString(),);(result);//Ccomment)

内联注释

#comment

注释到行尾(仅适用于忽略模式空白模式)

正则表达式选项

选择

意义

(?一)

不区分大小写的匹配(“忽略”大小写)

(?m)

多线模式;更改^和$,以便它们匹配任何行的开头和结尾

(?n)

仅捕获显式命名或编号的组

(?

编译为中间语言

(?

单线模式;更改“.”的含义,使其与每个字符匹配

(?x)

从模式中消除未转义的空格

(?r)

从右到左搜索;无法在中游指定

猜你喜欢
不容错过