正则表达式

正则表达式是一个用于匹配、提取、替换的包含元字符的字符串。
正则表达式中用\进行转义。

基本元字符

.  任意的一个非换行字符
[] 集合匹配,表示匹配一个出现在[]中的一个字符
|  或:a|ab,敏感词:病毒|大麻
() 提高优先级实现分组,abc->a(bc)

限定元字符

+    紧跟在+前面的字符出现>=1次
*    紧跟在*前面的字符出现>=0次
?    紧跟在?前面的字符出现0或1次
{n}  [0123456789]{11}表示数字出现11次
{n,} [5-9]{8,}表示5-9之间的数字出现>=8次,{1,}等价于+
{n,m}[0-9]{3,6}表示0-9之间的数字出现3-5次
\w   匹配包括下划线在内的任意单词字符(包括汉字),约等价于[0-9a-zA-Z_]
\W   匹配任何非单词字符,约等价于[^0-9a-zA-Z_]
\s   匹配任何空白字符包括换页符、回车符、换行符、制表符、垂直制表符,
     等价于[\f\r\n\t\v]
\S   匹配任何非空白字符,等价于[^\f\r\n\t\v]
\d   匹配一个数字字符,等价于[0-9]
\D   匹配一个非数字字符,等价于[^0-9]

匹配邮箱示例代码:

static void Main(string[] args)
{
    WebClient wc = new WebClient();
    wc.Encoding = Encoding.UTF8;
    string html = wc.DownloadString("http://tieba.baidu.com/p/2314539885");
    //【正则表达式1】
    //string regex = @"\w+@\w+(\.\w+)+";
    //【正则表达式2(改进版)】
    string regex = @"[0-9a-zA-Z_\-\.]+@[0-9a-zA-Z_]+(\.[0-9a-zA-Z_]+)+";
    MatchCollection mc = Regex.Matches(html, regex);
    int i = 0;
    foreach (Match match in mc)
    {
        Console.WriteLine(i++ + "," + match.Value);
    }
    Console.ReadKey();
}

示例正则表达式解析:

示例邮箱地址:99999@china.com
正则表达式1:@"\w+@\w+(\.\w+)+"
正则表达式1格式解析:
①@符号表示对字符串中的\进行转义
②\w+ 匹配"99999"部分
③@ 匹配"@"
④\w+ 匹配"china"部分
⑤(\.\w+) 匹配".com"部分
⑥(\.\w+)+ 匹配至少一个".com"部分,当邮箱是99999@china.vip.com格式时也能匹配到
⑦表达式1有个问题,即可以匹配到"12345@qq.com楼主好人",因为\w可以匹配汉字,因此有了改进版的表达式2

正则表达式2:@"[0-9a-zA-Z_\-\.]+@[0-9a-zA-Z_]+(.[0-9a-zA-Z_]+)+"
正则表达式2格式解析:
①@符号表示对字符串中的\进行转义
②[0-9a-zA-Z_\-\.]+ 匹配"99999"部分
③@ 匹配"@"
④[0-9a-zA-Z_]+ 匹配"china"部分
⑤(\.[0-9a-zA-Z_]+) 匹配".com"部分
⑥(\.[0-9a-zA-Z_]+)+ 匹配至少一个".com"部分,当邮箱是99999@china.vip.com格式时也能匹配到
⑦表达式2不能匹配到"12345@qq.com楼主好人",能匹配到"12345@qq.com",比表达式1更精确

^和$

once表示只匹配那些以"once"开头的字符串,once[0-9][a-z]end表示只匹配以"once[0-9][a-z]end"开头的字符串。
用两个同样的匹配规则,一个加、一个不加进行匹配结果对比,更容易理解,示例代码如下:

public void TestStart()
{
    int i = 0;
    bool flag;
    string input = "once1aend";
    string regex = @"once[0-9][a-z]end";
    string regexStart = @"^once[0-9][a-z]end";
    Console.WriteLine("【"+input + "匹配" + regexStart + "的结果:】");
    flag = Regex.IsMatch(input, regex);
    Console.WriteLine("是否匹配=" + flag);
    MatchCollection mc = Regex.Matches(input, regexStart);
    foreach (Match match in mc)
    {
        Console.WriteLine(i++ + "," + match.Value);
    }
    Console.WriteLine("【" + input + "匹配" + regex + "的结果:】");
    flag = Regex.IsMatch(input, regex);
    Console.WriteLine("是否匹配=" + flag);
    Console.WriteLine("匹配项:");
    mc = Regex.Matches(input, regex);
    i = 0;
    foreach (Match match in mc)
    {
        Console.WriteLine(i++ + "," + match.Value);
    }
    Console.ReadKey();
}

运行结果如图:
这里写图片描述
将变量input的值修改为"once1aend666",运行结果如图:
这里写图片描述
将变量input的值修改为"once1aend666once2bend",运行结果如图:
这里写图片描述
将变量input的值修改为"666once1aend",运行结果如图:
这里写图片描述
^对比结论:

  • "^once[0-9][a-z]end"只能匹配以此开头的字符串,若字符串中有多条内容符合"once[0-9][a-z]end"格式,也只能匹配到一条,匹配结果是符合匹配格式的字符串开头,建议使Regex.Match()。
  • "once[0-9][a-z]end"可以匹配到字符串中满足此格式的任意位置的内容,而且可以匹配到多条,建议使用Regex.Matchs()。

once$表示只匹配那些以"once"结尾的字符串,once[0-9][a-z]end$表示只匹配以"once[0-9][a-z]end"结尾的字符串。
用两个同样的匹配规则,一个加$、一个不加$进行匹配结果对比,更容易理解,示例代码如下:

public void TestEnd()
{
    int i = 0;
    bool flag;
    string input = "once1aend";
    string regex = @"once[0-9][a-z]end";
    string regexEnd = @"once[0-9][a-z]end$";
    Console.WriteLine("【" + input + "匹配" + regexEnd + "的结果:】");
    flag = Regex.IsMatch(input, regexEnd);
    Console.WriteLine("是否匹配=" + flag);
    Console.WriteLine("匹配项:");
    //【改进】匹配^...或...$只有一个结果,所以用Match()而不是Matchs()
    Match match = Regex.Match(input, regexEnd);
    Console.WriteLine(i++ + "," + match.Value);
    Console.WriteLine("---------------------------------");
    Console.WriteLine("【" + input + "匹配" + regex + "的结果:】");
    flag = Regex.IsMatch(input, regex);
    Console.WriteLine("是否匹配=" + flag);
    Console.WriteLine("匹配项:");
    MatchCollection mc = Regex.Matches(input, regex);
    i = 0;
    foreach (Match obj in mc)
    {
        Console.WriteLine(i++ + "," + obj.Value);
    }
}

运行结果如图:
这里写图片描述
将变量input的值修改为"once1aend666",运行结果如图:
这里写图片描述
将变量input的值修改为"once1aend666once2bend",运行结果如图:
这里写图片描述
将变量input的值修改为"666once1aend",运行结果如图:
这里写图片描述
$对比结论:

  • "once[0-9][a-z]end$"只能匹配以此结尾的字符串,若字符串中有多条内容符合"once[0-9][a-z]end"格式,也只能匹配到一条,匹配结果是符合匹配格式的字符串结尾,建议使Regex.Match()。
  • "once[0-9][a-z]end"可以匹配到字符串中满足此格式的任意位置的内容,而且可以匹配到多条,建议使用Regex.Matchs()。

once$表示只匹配"once"字符串,once[0-9][a-z]end$表示只匹配"once[0-9][a-z]end"字符串。
示例代码:

public void TestStratEnd()
{
    int i = 0;
    bool flag;
    string input = "once1aend";
    string regex = @"once[0-9][a-z]end";
    string regexStartEnd = @"^once[0-9][a-z]end$";
    Console.WriteLine("【" + input + "匹配" + regexStartEnd + "的结果:】");
    flag = Regex.IsMatch(input, regexStartEnd);
    Console.WriteLine("是否匹配=" + flag);
    Console.WriteLine("匹配项:");
    Match match = Regex.Match(input, regexStartEnd);
    Console.WriteLine(i++ + "," + match.Value);
    Console.WriteLine("--------------------------------");
    Console.WriteLine("【" + input + "匹配" + regex + "的结果:】");
    flag = Regex.IsMatch(input, regex);
    Console.WriteLine("是否匹配=" + flag);
    Console.WriteLine("匹配项:");
    MatchCollection mc = Regex.Matches(input, regex);
    i = 0;
    foreach (Match obj in mc)
    {
        Console.WriteLine(i++ + "," + obj.Value);
    }
}

运行结果如图:
这里写图片描述
将变量input的值修改为"once1aend666",运行结果如图:
这里写图片描述
将变量input的值修改为"once1aend666once2bend",运行结果如图:
这里写图片描述
将变量input的值修改为"666once1aend",运行结果如图:
这里写图片描述
^$对比结论:

  • "^once[0-9][a-z]end$"只能匹配符合"once[0-9][a-z]end"格式且不包含任何其他字符的字符串,建议使Regex.Match()。
  • "once[0-9][a-z]end"可以匹配到字符串中满足此格式的任意位置的内容,而且可以匹配到多条,建议使用Regex.Matchs()。
  • 当我们需要判断字符串是否是整数、小数类型时,可以使用^$来帮助我们。

匹配HTML标签

使用"<html><body></body></html>"来匹配"<.+>",只能匹配到一个满足项,"html><body></body></html"都被匹配到.+中了,所以只有一个满足项。截图如下:

这里写图片描述

如果我们相匹配完整的HTML标签时,内容中不应该有">",改进后,使用<html><body></body></html>来匹配"<[^>]+>",能匹配到四个满足项,截图如下(实际中遇到类似情况知道怎么去改进就行了):

这里写图片描述

使用分组

如果我们在正则提取过程中希望得到匹配项,同时需要使用匹配项中的某一部分的时候,使用分组可以方便的实现这个功能,只需要用圆括号把需要的部分括起来即可,例如:(.+)。
分组编号及使用:

  • Match对象有一个Groups的属性,Groups存储正则表达式匹配到的组
  • 根据需要加括号对数据进行分组,括号必须成对
  • 从左往右数"(",索引从1开始
  • 第0组是字符串本身,m.Groups[0].Value==m.Value
  • 匹配完成后使用Groups[index]即可访问所需的内容

示例代码如下:

public void TestGroup()
{
    string regex = @"^(([0-9]+)([a-z]+))([0-9]+)$";
    string input = "123abc456";
    Match match = Regex.Match(input, regex);
    for (int i = 0; i < match.Groups.Count; i++)
    {
        Console.WriteLine("match.Groups[" + i + "]=" +match.Groups[i].Value);
    }
    Console.ReadKey();
}

运行结果如图:
这里写图片描述
指定分组的名称

通过分组我们可以提取到匹配项,但是上面只讲了使用索引来提取指定的内容,如果分组很多的话索引值就不好数了,除此之外我们还可以使用(?< xxx>.+)为分组指定名称,xxx是指定的分组名称,.+是分组内容。
注意:指定名称的分组会排在未指定名称分组的后边
在这里插入图片描述
在这里插入图片描述

贪婪模式

正则表达式默认匹配尽可能多,示例代码如下:

public void TestGreed()
{
    string input = "1234567890";
    string regex = @"^(\d+)(\d+)(\d+)$";
    Match match = Regex.Match(input, regex);
    for (int i = 0; i < match.Groups.Count; i++)
    {
        Console.WriteLine("match.Groups[" + i + "]=" +match.Groups[i].Value);
    }
    Console.ReadKey();
}

运行结果如图:
这里写图片描述
默认匹配尽可能多,所以Groups[1]匹配了最多,只给Groups[2]、Groups[3]留了一个数字,留一个是因为Groups[2]、Groups[3]对应(\d+),+表示数量>=1,最小值是1,所以留了一个。
如果正则表达式变为string regex = @"^(\d+)(\d*)(\d*)$";,那Groups[2]、Groups[3]的值均为空,因为*表示数量>=0,最小值是0,所以一个不留。
运行结果如图:
这里写图片描述
?可以取消贪婪,取消第一个分组的贪婪模式后,第二个分组的权限会变得最大(即第二分组贪婪),string regex = @"^(\d+?)(\d+)(\d+)$";,运行结果如图:
这里写图片描述
一般在写正则表达式的时候,不需要去考虑贪婪的问题,直接写就行。如果出现了匹配结果比预期多的情况,一般是贪婪造成的,再去考虑取消贪婪的问题。
匹配一个整数

整数分为:0、正整数、负整数,不允许出现0123这种形式
包含匹配:“0|-?[1-9][0-9]*”,123abc456可以匹配到两个满足条件的结果——123和456,实际上123abc456不是正确的数字格式。
绝对匹配:"0|-?[1-9][0-9]*$",123abc456可以匹配到满足条件的结果456,因为"0|-?[1-9][0-9]*$“等价于”(0)|(-?[1-9][0-9]*$)",如果写成"(0|-?[1-9][0-9]*$)"那123abc456就匹配不到了
正则替换

public void TestReplace()
{
    string input = "ab------c---d---ef----g";
    string result = Regex.Replace(input, @"-+", "-");
    Console.WriteLine("input=" + input);
    Console.WriteLine("result=" + result);
}

运行结果如图:
这里写图片描述
引用匹配到的数据:
要引用前面匹配到的结果,可以使用$引用前面匹配到的部分,$0表示把前面匹配到的第0组拿过来,第0组是字符串本身,m.Groups[0].Value==m.Value,如果继续加"()"的话,可以用$1、$2…来引用

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        Label1.Text = "http://www.baidu.com123 4 5 6http://www.csdn.net/";
    }
}
protected void btnReplace_Click(object sender, EventArgs e)
{
    string input = "http://www.baidu.com123 4 5 6http://www.csdn.net/";
    //不加()
    string result = Regex.Replace(input, @"http://[\w\.]+\.[a-z]+", "<a href=\"$0\">$0</a>");
    //加上(),result==resultAdd
    string resultAdd = Regex.Replace(input, @"(http://[\w\.]+\.[a-z]+)", "<a href=\"$1\">$1</a>");
    Label1.Text = result;
}

点击替换按钮之前:
这里写图片描述
点击替换按钮之后:
这里写图片描述
注意

一般,正则表达式很难一次就写得完美,我们可以先写出来测试一下,然后根据匹配到的结果,逐步精细化正则表达式以达到自己的目的。
测试正则表达式可以使用RegexTester工具。
注意

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

changuncle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值