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。

相關文章
相關標籤/搜索