欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C# 正则表达式进阶

程序员文章站 2023-11-18 17:42:10
.net 中的正则表达式是基于 perl 5 的正则表达式。超时从 .net framework 4.5 开始,正则表达式支持在匹配操作中指定超时时间。如果匹配超时,就会抛出 regexmatchti...

.net 中的正则表达式是基于 perl 5 的正则表达式。

超时

从 .net framework 4.5 开始,正则表达式支持在匹配操作中指定超时时间。如果匹配超时,就会抛出 regexmatchtimeoutexception。

所有方法都增加了带超时时间参数的重载:

public static match match(string input, string pattern, regexoptions options, timespan matchtimeout);

public static matchcollection matches(string input, string pattern, regexoptions options, timespan matchtimeout);

public static string replace(string input, string pattern, string replacement, regexoptions options, timespan matchtimeout);

public static string[] split(string input, string pattern, regexoptions options, timespan matchtimeout);

如果应用程序需要处理任意的正则表达式(例如在高级搜索对话框中)则务必使用该参数以防止一些恶意的正则表达式导致无限计算。

编译正则表达式

regexoptions.compiled 选项将会使 regex 实例通过轻量级的代码生成器动态地构建并编译针对特定正则表达式的代码,提高匹配速度。

模式修正符

模式修正符不仅可以打开,还可以关闭。如下示例,先打开忽略大小写,再关闭忽略大小写,所以匹配结果是 aa。

regex.match("aaaa", "(?i)a(?-i)a").value;  // aa

零宽断言

现在要写一个用于验证密码是否符合要求的正则表达式,要求是至少包含一个数字。

这个很简单,如下就可以了

regex.ismatch("12345678", "\d");

现在加一个条件,长度要大于 6 位。似乎用一个正则无法实现。

其实是可以的,用零宽断言中的 正向先行断言 就可以了。

正向先行断言 (?=exp),一般用来匹配 exp 之前的内容。例如下面个例子,要取出姓名,需要匹配 , 之前的内容。

regex.match("姓名张三,男,30 岁", "(?<=姓名).*?(?=,)").value; // 张三

其实,正确的理解是:正向先行断言,匹配成功之后,会退回起始位置,然后继续之后的匹配。

这里最重要的一点是,匹配成功以后退回起始位置,所以,对它正确的理解是,一个前向条件判断。

那么上面的密码至少包含一个数字,且长度大于 6 就好实现了:

regex.ismatch("abcde6", @"(?=.*\d).{6,}");

我们再增加一点难度,密码要求符合如下条件:

  • 至少 8 位
  • 至少包含一个数字
  • 至少包含一个小写字母
  • 至少包含一个大写字母
string pattern = @"(?=.*\d)(?=.*[a-z])(?=.*[a-z]).{8,}";
regex.ismatch("12345678", pattern); // false
regex.ismatch("1234567a", pattern); // false
regex.ismatch("123456aa", pattern); // true

分割字符串

分割字符串分隔符不会包含在结果中,若要将分隔符包含在结果中,则可以将表达式包含在正前向条件中。

foreach (string s in regex.split("onetwothree", "(?=[a-z])"))
  console.writeline(s);
  
// one
// two
// three

分组

正则表达式中可以通过 \n 语法来引用索引为 n 的分组。

var m = regex.matches("pop pope peep", @"\b(\w)\w+\1\b");

// pop
// peep

命名捕获分组语法:
(?'组名'表达式) 或 (?<组名>表达式)

引用命名分组语法:
\k'组名' 或 \k<组名>

替换并分割文本

替换字符串可以通过 $0 作为替代结构访问原始的匹配。$1、$2 访问任意捕获的分组。对于命名分组,可以通过 ${name} 的方式进行访问。

给所有数字加上 <>:

console.writeline(regex.replace("1 + 11 = 12", @"\d+", @"<$0>"));
// <1> + <11> = <12>

matchevaluator 委托

replace 方法有一个重载,使用 matchevaluator 委托作为参数,替代 replacement。该委托将对每个匹配执行一次,并使用其返回结果替换原字符串中的值。

matchevaluator 委托定义:

public delegate string matchevaluator(match match);

示例:

console.writeline(regex.replace("1 + 11 = 12", @"\d+", m => (int.parse(m.value) * 10).tostring()));

// 10 + 110 = 120

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。