C#入门分享(七)——正则表达式与字符串搜索

1 正则表达式

  正则表达式提供了功能强大、灵活而又高效的方法来处理文本。正则表达式的全面模式匹配表示法能够快速地分析大量的文本以找到特定的字符模式;提取、编辑、替换或删除文本子字符串;或将提取的字符串添加到集合以生成报告。对于处理字符串(例如 HTML 处理、日志文件分析和 HTTP 标头分析)的许多应用程序而言,正则表达式是不可缺乏的工具。 

  .NET 框架正则表达式并入了其余正则表达式实现的最多见功能,被设计为与 Perl 5 正则表达式兼容,.NET 框架正则表达式还包括一些在其余实现中还没有提供的功能,.NET 框架正则表达式类是基类库的一部分,而且能够和面向公共语言运行库的任何语言或工具一块儿使用。

2 字符串搜索

  正则表达式语言由两种基本字符类型组成:原义(正常)文本字符和元字符。正是元字符组为正则表达式提供了处理能力。当前,全部的文本编辑器都有一些搜索功能,一般能够打开一个对话框,在其中的一个文本框中键入要定位的字符串,若是还要同时进行替换操做,能够键入一个替换字符串,好比在Windows操做系统中的记事本、Office系列中的文档编辑器都有这种功能。这种搜索最简单的方式,这类问题很容易用String类的String.Replace()方法来解决,但若是须要在文档中识别某个重复的,该怎么办?编写一个例程,从一个String类中选择重复的字是比较复杂的,此时使用语言就很适合。

  通常表达式语言是一种能够编写搜索表达式的语言。在该语言中,能够把文档中要搜索的文本、转义序列和特定含义的其余字符组合在一块儿,例如序列\b表示一个字的开头和结尾(子的边界),若是要表示正在查找的以字符th开头的字,就能够编写通常表达式\bth(即序列字符界是-t-h)。若是要搜索全部以th结尾的字,就能够编写th\b(序列t-h-字边界)。可是,通常表达式要比这复杂得多,例如,能够在搜索操做中找到存储部分文本的工具性程序(facility)。

3 .NET 框架的正则表达式类

  下面经过介绍 .NET 框架的正则表达式类,熟悉一下.NET框架下的正则表达式的使用方法。html

     (1)在C#中使用.NET通常表达式引擎正则表达式

           下面将经过一个样例的开发,执行并显示一些搜索的结果,说明通常表达式的一些特性,以及如何在C#中使用.NET通常表达式引擎.说明使用字符串时应在前面加上符号@.编程

           String Text=@"I can not find my position in Beijing";框架

           把这个文本称为输入字符串,为了说明通常表达式.NET类,本文先进行一次纯文本的搜索,此次搜索不带任何转义序列或通常表达式命令,把这个搜索字符串称为模式.使用通常表达式和上面声明的变量Text,编写出下面的代码:编辑器

      在这段代码中,使用了System.Text.RegularExpressions名称空间中Regex类的静态方法Match( ).这个方法的参数是一些输入文本、一个模式和RegexOptions每句中的一组可选标志.Matches( )返回MatchCollection,每一个匹配都用一个Match对象来表示.在上面的代码中,只是在集合中迭代,使用Match类的Index属性,返回输入文本中匹配所在的索引.运行这段代码,将获得1个匹配项.
      通常集合的功能主要取决于模式字符串.缘由是模式字符串不只仅包含纯文本.如前所述.还包含元字符和转义序列,元字符是给出命令的特殊字符,而转义序列的工做方式与C#的转义序列相同,它们都是以反斜杠开头的字符,具备特殊的含义.例如,假定要查找以n开头的字,就可使用转义序列,它表示一个字的边界(字的边界是以某个字母数字标的字符开头,或者后面是一个空白字符或标点符号),下面编写以下代码:
函数

        String Pattern = @"n";
        MatchCollection Matches = Regex.Matches( Text,Pattern,RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture );
工具

      要在运行时把传递给.NET通常表达式引擎,反斜杠不该被C#编译器解释为转义序列.若是要查找以序列ion结尾的字,可使用下面的代码:网站

        String Pattern = @"ion";spa

      若是要查找以字母n开头,以序列ion结尾的全部字,须要一个以n开头,以ion结尾的中间内容怎么办?须要告诉计算机n和ion中间的内容能够是任意长度的字符,只要字符不是空白便可,正确的模式以下所示:操作系统

        String Pattern = @"nS*ion";

     (2)特定字符或转义序列

      大多数重要的正则表达式语言运算符都是非转义的单个字符.转义符 (单个反斜杠)通知正则表达式分析器反斜杠后面的字符不是运算符.例如,分析器将星号 ( * ) 视为重复限定符,而将后跟星号的反斜杠 ( * ) 视为 Unicode 字符 002A.
      使用通常表达式要习惯的一点是,查看像这样怪异的字符序列,但这个序列的工做是很是逻辑化的.转义序列S表示任何不适空白的字符.*称为数量词,其含义是前面的字符能够重复任意次,包括0次.序列S*表示任何不适空白的字符.所以,上面的模式匹配于以n开头,以ion结尾的任何单个字.下表中列出的字符转义在正则表达式和替换模式中都会被识别.

      下表是经常使用的特定字符或转义序列:

     若是要搜索一个元字符,也能够经过带有反斜杠的转义字符来表示。例如,.表示除了换行字符之外的任何字符,而\.表示一个点。
     能够把可替换的字符放在方括号中,请求匹配包含这些字符。例如,[1|c]表示字符能够是1或者是c。若是要搜索map或者man,可使用序列"ma[n|p]"(仅指引号内字符,下面雷同)。在方括号中,也能够制定一个范围,例如"[a-z]"表示全部的小写字母(使用连字号 (-) 容许指定连续字符范围),"[B-F]"表示B到F之间的全部大写字母,"[0-9]"表示一个数字,若是要搜索一个整数(该序列只包含0到9的字符),就能够编写"[0-9]+"(注意,使用+字符表示至少要有这样一个数字,但能够有多个数字,因此九、83和3443等都是匹配的。)
      下面看看通常表达式的结果,编写一个实例RegularExpressionsZzy。创建几个通常表达式,显示其结果,让用户了解一下表达式是如何工做的。

  该实例的核心是一个方法WriteMatches(),它把MatchCollection中的全部匹配以比较详细的方式显示出来。对于每一个匹配,它都会显示该匹配在输入字符串中所在的索引,匹配的字符串和一个略长的字符串,其中包含输入文本中至多8个外围字符,其中至少有5个字符放在匹配的前面,至多5个字符放在匹配的后面(若是匹配的位置在输入文本的开头或结尾5个字符内,则结果中匹配先后的字符就会少于4个)。换言之,靠近输入文本末尾的匹配应是"and messaging ofd",匹配的先后各有5个字符,但位于输入文本的最后一个字上的匹配就应是"g of data",匹配的字后只有一个字符。由于在该字符的后面是字符串的结尾。这个长字符串能够更清楚地代表通常表达式是在什么地方查找到匹配的:

      在这个方法中,处理过程是肯定在较长的字符串中有多少个字符能够显示,而无需超限输入文本的开头或结尾。注意在Match对象上使用了另外一个属性Value,它包含标识该匹配的字符串,并且,RegularExpressionsZzy只包含名为Find_po,Find_n等的方法,这些方法根据本文执行某些搜索操做。

  (3) 正则表达式选项

  可使用影响匹配行为的选项修改正则表达式模式。能够经过两种基本方法设置正则表达式选项:其一是能够在 Regex(pattern, options) 构造函数中的 options 参数中指定,其中 options 是 RegexOptions 枚举值的按位"或"组合;其二是使用内联 (?imnsx-imnsx:) 分组构造或 (?imnsx-imnsx) 其余构造在正则表达式模式内设置它们。

  在内联选项构造中,一个选项或一组选项前面的减号 (-) 用于关闭这些选项。例如,内联构造 (?ix-ms) 将打开 IgnoreCase 和 IgnorePatternWhiteSpace 选项而关闭 Multiline 和 Singleline 选项。

   下表是RegexOptions 枚举的成员以及等效的内联选项字符 :

     例如,Find_po在字开头处查找以"po"开头的字符串:

     这段代码还使用了名称空间RegularExpressions:

     (4)匹配、组和捕获

  通常表达式的一个很好的特性是能够把字符组合起来,方式与C#中的复合语句同样。在C#中,能够经过把任意数量的语句放在花括号中的方式把它们组合在一块儿。其结果就像一个复合语句那样。在通常表达式模式中,也能够把任何字符组合起来(包括元字符和转义序列),像处理一个字符那样处理它们。惟一的区别是要使用圆括号,而不是花括号,获得的序列成为一个组。 

  例如,模式"(an)+"定位序列an的任以重复。量词+只应用于它前面的一个字符,但由于咱们把字符组合起来了,因此它如今把重复的an做为一个单元来对待。"(an)."应用到输入文本"bananas came to Europe late in the annals of history"上,会从bananas中选择出anan。另外一方面,若是使用an+,则将从annals中选择ann,从bananas中选择出两个an。为何(an)+选择的是anan,而没有把单个的an做为一个匹配。匹配规则是不能重复的,若是有可能重复,在默认状况下就选择较长的匹配。

  可是,组的功能要比这强大得多。在默认状况下,把模式的一部分组合为一个组时,就要求通常表达式引擎记住能够按照这个组来匹配,也能够按照整个模式来匹配。换言之,能够把组看成一个要匹配的模式,若是要把字符串分解为各个部分,这种模式就是很是有效的。 
例如,URI的格式是"<protocol>://<address>:<port>",其中端口是可选的。它的一个样例是http://www.comprg.com.cn:8080。假定要从一个URI中提取协议、地址和端口,并且紧邻URI的后面可能有空白(但没有标点符号),就可使用下面的表达式:"\b(\S+)://(\S+)(?::(\S+))?\b"

  该表达式的工做方式以下:首先,前导和尾部的\b序列确保只须要考虑彻底是字的文本部分,在这个文本部分中,第一组"(\S+)://"会选择一个或多个不适空白的字符,其后是"://"。在HTTPURI的开头会选择出http://。花括号表示把http存储为一个组。后面的"(\S+)"则在上述URI中选择www. comprg.com.cn,这个组在遇到词的结尾时或标记另外一个组的冒号"(:)"时结束。

  下一个组选择端口(本例是:8080)。后面的?表示这个组在匹配中是可选的,若是没有:xxxx,也不会妨碍匹配的标记。

  这是很是重要的,由于端口在URI中通常不指定,实际上,在大多数状况下,URI是没有端口号的。可是,事情会比较复杂。若是要求冒号能够出现,也能够不出现,但不但愿把这个冒号也存储在组中。为此,能够嵌套两个组:内部的"(\S+)"组选择冒号后面的内容(本例中是8080),外面的组包含内部的组,后面是一个冒号,该冒号又在序列"?:"的后面。这个序列表示该组不该保存(只须要保存"8080",不须要保存":8080")。不要把这两个冒号混淆了,第一个冒号是序列"?:"的一部分,表示不保存这个组,第二个冒号是要搜索的文本。

  在这个字符串上运行该模式:I always visit http://www. comprg.com.cn 获得的匹配是http://www. comprg.com.cn。在这个匹配中,仅提到了三个组,还有第四个组表示匹配自己。理论上,每一个组均可以选择0次、1次或者屡次匹配。单个的匹配就称为捕获。在第一个组"(\S+)",有一个捕获http。第二个组也有一个捕获www. comprg.com.cn,但第三个组没有捕获,由于在这个URI中没有端口号。注意该字符串在其自己上包含第二个http://。虽然它匹配于第一个组,但不会被搜索出来,由于整个搜索表达式不匹配于这部分文本。
      再好比下面这个例子,如下代码示例使用 Match.Result 来从 URL提取协议和端口号。例如,"http://www.yahoo.com.cn:8080/index.html"将返回"http:8080"。

     .NET下的正则表达式就先介绍到这,它们在C#编程中有着普遍的应用,但愿你们能在常用的基础上逐渐掌握。下一篇博客为你们介绍C#中的LINQ。

相关文章
相关标签/搜索