C#开发训练营

第13课:正则表达式(2)

本课继续讨论正则表达式的相关操作,主要包括反向引用和环视,最后还会封装一些常用操作,如E-mail地址格式验证、删除文本中的所有空白字符,以及15位和18位身份证号码规则的验证等操作。

反向引用

反向引用构造可更加方便地匹配重复内容,首先来看数字反向引用构造的应用。如文本中需要匹配连续的相同字符,可以参考如下代码。

C#
using System;
using System.Text.RegularExpressions;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string pattern = @"(\w)\1";
        tWeb.WriteLine(Regex.IsMatch("apple",pattern));  // True
        tWeb.WriteLine(Regex.IsMatch("orange",pattern)); // False
    }
}

本例,子表达式(\w)表示一个字母、数字或下画线字符,\1则表示与第一个子表达式相同,这样就定义了连续两个相同的字符模式。第一个输出中,apple中包含连续的p,匹配结果为True;第二个输出中,orange没有连续的相同字母,匹配结果为False。

如果自动的数字编号不够清晰,也可以为子表达式指定明确的编号,将上例中的模式修改为如下代码可以得到相同的运行结果。

C#
string pattern = @"(?<1>\w)\1";

代码中,在子表达式里使用?<n>格式定义数字编号。

除了使用数字命名子表达式,也可以使用文本形式的表达式名称,如将前面的示例修改为下面的模式也可以得到相同的结果。

C#
string pattern = @"(?<char1>\w)\<char1>";

使用反向引用构造,在匹配相同的内容时,除了连续的内容,也可以在文本中匹配对称的内容,比如,对HTML代码的检索时匹配h1到h6的元素,下面的代码演示了相关应用。

C#
using System;
using System.Text.RegularExpressions;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string pattern = @"<h([1-6])>.+?<\/h\1>";
        string code = @"<h1>标题一</h1>
<p>段落一</p><h2>标题二</h2><p>段落二</p><h3>标题三</h3>";
        foreach(Match m in Regex.Matches(code, pattern))
        {
            tWeb.WriteLine(Server.HtmlEncode(m.Value));
        }
    }
}

运行代码会匹配的h1、h2和h3元素,代码中还使用了Server.HtmlEncode()方法将匹配内容进行HTML转码,最终显示结果如图1。

图1

环视

环视的功能是匹配一个位置,然后获取此位置前面的内容(向前查看)或后面的内容(向后查看)。

Email地址的格式是“<用户名>@<域名>”,如果需要通过正则表达式读取用户名和域名部分,则可以通过环视来实现,如下面的代码,使用向前查看获取Email地址中的用户名部分。

C#
using System;
using System.Text.RegularExpressions;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string pattern = @".+(?=@)";
        string s = "xxx@yyy.zzz";
        Match match = Regex.Match(s, pattern);
        tWeb.WriteLine(match.Value);
    }
}

模式中的(?=@)部分定义了环视规则,?=表示向前查看,而@则表示@字符所在位置,执行代码会显示@前的内容,即Email地址中的用户名部分xxx。

向后查看时使用?<=限定符,如下面的代码会显示Email地址中的域名部分。

C#
using System;
using System.Text.RegularExpressions;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string pattern = @"(?<=@).+";
        string s = "xxx@yyy.zzz";
        Match match = Regex.Match(s, pattern);
        tWeb.WriteLine(match.Value);
    }
}

执行代码会显示yyy.zzz,即@符号后面的内容。

环视限定符还包括?!(否定式向前查看)和?<!(否定式向后查看)。在模式中,如果需要查询单独的go单词,可以使用@"\bgo\b"模式,如果需要查询以go开头的单词,但又不是单独的go单词时,可以使用如下代码。

C#
using System;
using System.Text.RegularExpressions;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string pattern = @"\bgo(?!\b)";
        tWeb.WriteLine(Regex.IsMatch("go",pattern));  // False
        tWeb.WriteLine(Regex.IsMatch("goto", pattern));  // True
    }
}

代码中定义的模式,左边是字边界也就是指定单词应以go开始,在子表达式(?!\b)中,表示go后不应是字边界,也就是说不能是独立的单词go。

?<!(否定式向后查看)表示不能以指定的内容开始,如下面的代码。

C#
using System;
using System.Text.RegularExpressions;

public partial class Test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string pattern = @"\b\w+(?<!ing)\b";
        tWeb.WriteLine(Regex.IsMatch("going",pattern));  // False
        tWeb.WriteLine(Regex.IsMatch("goto", pattern));  // True
    }
}

代码中的模式会匹配不以ing结束的单词,going匹配结果为False,goto匹配结果为True。

综合应用

熟练掌握正则表达式需要大量的实践,在定义模式时也有很多技巧,一个功能也可以使用多种实现方法,比如,在帮助文档的示例中判断E-mail地址的模式定义如下:

C#
string pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";

其中,除了必须的@和圆点(.)符号,其它部分定义了不应包含空字符。这里,也可以要求只能包含哪些字符,如下面的模式。

C#
string pattern = @"^[a-zA-Z0-9](\w+\.)*\w+@(\w+\.)+\w+$";

开发中,可以根据需要定义或扩展检查规则。本书代码中会使用tStr.IsEmail()方法判断文本是否为有效的E-mail地址。

此外,需要注意的是,只判断E-mail地址并不能邮箱是否真正有效;在真实的应用环境中,可以通过发送确认邮件的形式判断邮箱的有效性。

接下来将封装一些常用的功能,包括删除文本中的所有空白字符,以及身份证号码验证的相关操作。