一篇文章學會C#的正則表達式

原文地址:https://blog.csdn.net/qq_38507850/article/details/79179128

正則表達式一句話概括就是用來對字符串根據自己的規則進行匹配的,可以匹配(返回)出符合自己要求的匹配結果,有人說字符串類的函數也可以,確實是這樣,但是字符串的函數對於字符串更多的是處理層面,且不是那麼的靈活。而正則表達式的匹配就非常靈活了。


一,C#中使用正則表達式經常使用的類(Regex,MatchCollection,Match)

基本上我們使用正則表達式都會使用到這三個類:

Regex類,這是一個作用於匹配的類,用來定義一個匹配對象,即以何種規則進行匹配。

MatchCollection,這是一個作用於匹配結果集的類,因爲既然涉及到匹配那麼就可能會匹配出多個結果,這多個結果就可以賦值給匹配結果集(MatchCollection)的對象。

Match,這是一個匹配結果的類,多個匹配結果(Match)組成一個匹配結果集(MatchCollection)。

以上三個類都在命名空間:System.Text.RegularExpressions下,所以在使用正則表達式的時候我們要先引用這個命名空間。


二,正則表達式的簡單使用案例:從一個字符串中匹配出所有的郵箱。

比如我們要從字符串“23794大富科世紀東方了[email protected]@163.comsdfjsd  [email protected]*&*&*2”中匹配出所有的郵箱。這要通過字符串自帶函數來解決就有點難了,使用正則表達式就非常容易。

解法如下:

//聲明這個要被匹配的字符串

string content="23794大富科世紀東方了[email protected]@163.comsdfjsd  [email protected]*&*&*2";

//聲明匹配規則對象,並設定匹配規則

Regex regex=new Regex(@"[a-zA-Z0-9]+@[a-zA-z0-9]+\.com");//構造方法裏面的參數就是匹配規則。

//聲明匹配結果集對象

MatchCollection matchs;

//調用匹配多個出多個結果的方法進行匹配,將返回的匹配結果集對象賦值給matchs

matchs=regex.Matches(content);

//輸出所有匹配結果

foreach(Match match in matchs)

{

Console.WriteLine(match);

}

Console.ReadKey();

輸出結果如下:


就這樣,我們將所有的郵箱匹配出來了,這就是正則表達式的使用的妙處,正則表達式其實最難的就是匹配規則。

下面我們詳細介紹正則表達式:


三,正則表達式的匹配規則介紹

所謂正則表達式的匹配規則其實就是一個字符串(充滿了各種通配符,各種符號的字符串),這個字符串用來說明要匹配到的字符串的規則,然後匹配過程就是根據自己定義的這個字符串進行匹配去了。

1,正則表達式的通配符。

(1)"\d"這個符號代表從0-9的數字字符。

(2)"\w"代表所有單詞字符,包括:大小寫字母a-z、數字0-9、漢字(其實我認爲是各國文字字符都可以但是身爲中國人應該只用到了漢字)、下劃線。

(3)"\s"代表任何空白字符,所謂空白字符就是打出來是空白的字符,包括:空格、製表符、換頁符、換行符、回車符等等。

(4)"\D"代表任何非數字字符。

(5)"\W"代表任何非單詞字符。

(6)"\S"代表任何非空白字符。

(7)"."代表除換行符(\n)之外的任何字符。

以上這些都是通配符,匹配的範圍可以說是很廣。需要特別說明的有三點:

第一:通配符"."因爲代表除\n之外的所有字符,所以我們在匹配的時候如果要匹配到"."就應該用"\."代替,如上面我們匹配郵箱的案例,因爲每個郵箱格式肯定是"[email protected]"裏面有一個點,這個點我就用"\."代替了,而不是直接用"."。

第二:既然我們所用的通配符都有"\"那麼在定義正則規則的時候我們肯定要在正則規則字符串前面加@,以表示所有的\都不轉義。

第三:正則表達式學起來非常容易,但是無論是幾年的程序員還是新手,寫正則表達式很容易出錯,這些錯誤筆者總結很大部分來源於通配符的使用,因爲通配符實在是表示的範圍比較廣,我們沒有辦法掌握,所以在實際使用過程中應該儘量減少通配符的使用而使用我下面介紹的(可選字符集)形式。

2,可選字符集

可選字符集就是定義在某個字符位上允許出現的字符,使用可選字符集的好處就是我們可以對匹配的結果進行更靈活的掌握,彌補了通配符匹配範圍太廣的不足。這就有點像我們國家對於某些敏感行業的控制,如金融。如果國家方對於這個行業不進行控制,那麼以我們國人的智慧不知道會出現多少詐騙犯,多少非法集資。所以我國對於金融是准入制的,就是給你拍照你才能開類似的金融公司,不給你拍照你就不能,這樣便於掌握。國家方只需要知道發出去多少牌照就知道有多少個金融的企業在運作。而我們的可選字符集與這個有異曲同工之妙。

可選字符集就是把某個字符位允許出現的字符用[]括起來,[]裏面的字符之外的其他字符不就不匹配。

下面介紹一些可選字符集的案例:

案例一:[abc],指定這個字符位可以出現a或者b或者c。

案例二:[123],指定這個字符位可以出現1或者2或者3。

案例三:[a-d],指定這個字符位可以出現a到d的任何字符之一。

案例四:[a-zA-Z],指定這個字符位可以出現大小寫字符的a-z

案例五:[a-t1-7],指定這個字符位可以出現小寫字母a-t,數字1-7中的任何一個字符。

案例六:[\w\s],指定這個字符位可以出現任何單字字符和任何空白字符之一。

備註:所以所謂的可選字符集就是指定一個字符位可以出現哪個字符。

那麼問題又來了,如果我們要指定除0之外的所有字符呢,這樣怎麼指定,難道把除0之外的字符都寫進可選字符集嗎,於是就誕生了一個新概念--反向字符集。

3,反向字符集:

反向字符集就是指定一個字符位是除某些字符之外的其他任何字符之一。

案例一:[^a],指定這個字符位可以出現除a之外的任何字符,注意是任何字符,不止是b-z。

案例二:[^abc],指定這個字符位可以出現除小寫字母abc之外的任何字符之一。

案例三:[^0-9],指定這個字符位可以出現除0到9之外的任何字符之一。

案例四:[^#],指定這個字符位可以出現除井號之外的任何字符之一。

案例五:[^\w],指定這個字符位可以出現除單詞字符之外的任何字符之一。

好,有了以上的知識我們就可以對很多種字符串形式進行匹配了,如我們可以從一個字符串中匹配出"thankyou",結尾是任何一從2到9個的結果。

案例:從字符串"ashdfkthankyou239523&(&(&(^^^#sjlsdfj29304jd"匹配出"thankyou"。

代碼:

//定義要被匹配的字符串

string content1="ashdfkthankyou239523&(&(&(^^^#sjlsdfj29304jd";

//定義匹配規則

Regex regex1=new Regex(@"thankyou[2-9]");

//聲明一個匹配結果集對象,並將匹配結果賦值給這個對象

MatchCollection matchs1=regex1.Matches(content1);

//輸出匹配結果集

foreach(Match match in matchs1)

{

Console.WriteLine(match);

}

Console.ReadKey();

輸出結果:


以上是通過對匹配字符串的每個字符位進行限定,那麼如果我們想要對幾個字符位進行限定,不想一個個的字符進行限定,這樣怎麼處理呢。

這就需要引入,“或”匹配和限定符了。

4,“或”匹配。

或匹配用來匹配可以出現的某個字符串子串。

案例一:thanyou|thankher,這個指定匹配出thankyou或者thankher。

案例二:(b|tr)ee,這個指定匹配出bee或者tree。

案例三:th(i|a)nk,這個指定匹配出thank或者think。

案例四:book one|two,這個指定匹配出,book one或者two。注意不是book one或者book two。

案例五:book (one|two),這個指定匹配出book one或者book two。

5,限定符

限定符用來限定字符(串)出現的次數。

"*"限定符:將前面的(一個字符)字符重複零次到多次。

注意是一個字符:如我們用ds*對字符串進行匹配,匹配的是d後面跟0到n個s,而不是0到n個ds。

"+"限定符:將前面的(一個字符)字符重複一次到多次。

注意:與*唯一的區別就是重複至少一次。

"?"限定符:將前面的(一個字符)重複零次到一次。

"{n}"限定符:將前面的字符重複n次。

"{n,}"限定符:將前面的字符重複至少n次。

"{n,m}"限定符:將前面的字符重複n到m次。

補充:

限定符的懶惰修飾符,以上的限定符默認都是重複儘量多,而在以上的符號後面加"?"就重複儘量少。

如:"*?"就是將前面的字符儘量少重複。

案例一:我們用"5*?"去匹配字符串"25255255525555"匹配出來的結果是:空,因爲*後面加了?,這樣的匹配規則就是匹配5零次到多次儘量少重複,這樣他當然就優先匹配出現零次,這樣匹配出來的結果全部是""。

案例二:我們用"5*"去匹配字符串"25255255525555"的話匹配出來的結果不是這樣了,因爲默認是儘量多重複,所以匹配出來的結果是:

25、255、2555、25555。

通過以上的學習大家肯定就理解了我前面匹配郵箱的那個正則表達式的意義。

下面接着講:


如果我們要匹配從字符串的開頭開始匹配,比如,我們要匹配某個字符串開頭的四個字符組成的子串,這個用正則表達式怎麼實現呢。

用目前的的正則表達式實現不了,當然,用字符串的函數能夠實現,但是如果要用正則表達式的話就得引入一個新概念---定位符。

6,定位符

定位符用來在正則規則中確定匹配對應的字符串的位置。

"^"定位符,^在定位符中代表匹配字符串的開頭位置,注意是開頭位置,不是字符串的第一個字符,可以理解爲字符串第一個位置之前的小空隙。

"$"定位符,$定位符代表匹配字符串的末尾位置,可以理解爲字符串最後一個字符末尾的空隙。

"\b"定位符,匹配單詞的邊界位置,也是空隙位置,單詞的空隙位置可以是:空格,逗號,句號,開頭,末尾,"-"等等。


有了定位符的概念我們就可以完成以上的對字符串四個字符的匹配了。

代碼:

Regex regex=new Regex(@"^\w{4}");


7,分組的概念

在匹配郵箱的時候我們也可以使用分組的概念,比如,我們要一次將郵箱全址、郵箱用戶名都匹配出來,就可以用到分組的概念。

此時匹配的正則表達式爲:@"([a-zA-Z0-9_]+)@([a-zA-Z0-9])+\.com"。

這個正則表達式我們用到了兩個括號,這兩個括號就是分組,將正則表達式分成了三組,第0組是整個匹配結果,第1組是第一個括號裏面的內容,第二組是第二個括號裏面的內容,顯而易見,第一個括號裏面的內容是用戶名(即郵箱地址的@前面的字符串是用戶名)。

這樣在匹配結果集的每個結果中都有可以通過訪問這些分組來訪問裏面的數據,通過結果Match對象的Groups屬性進行訪問。

下面是具體案例:從字符串"[email protected]   [email protected]"中匹配出郵箱和郵箱賬號。

代碼:

//定義匹配規則

Regex regex=new Regex(@"([a-zA-Z0-9_]+)@([a-zA-Z0-9])+\.com");

//定義要被匹配的字符串

string content="[email protected]   [email protected]";

//進行匹配,並返回結果集

MatchCollection matchs=regex.Matches(content);

//輸出結果

for(int i=0;i<matchs.Count;i++)

{

Console.WriteLine("匹配到的第{0}個郵箱結果是{1},對應用戶名是{2}",i+1,matchs[i],matchs[i].Groups[1]);

}

Console.ReadKey();

執行截圖:


上面的例子我使用了括號進行分組,把用戶名寫進了第一個括號內,這樣就分成了1組,要訪問用戶名的時候我只需要訪問特定結果的Groups分組集合裏面的下標爲1的元素即可。

事實上在正則表達式中只要出現括號我們就對括號裏面的內容開闢了分組,但是分組是額外需要資源的,所以在我們不得不用到小括號,而又不希望對這個括號裏面的匹配內容進行分組的時候我們就需要“非捕獲分組”這個概念。

比如在上面的匹配郵箱地址和郵箱用戶名的例子中我們希望括號裏面不匹配到用戶名的話正則表達式就可以這樣改:

正則表達式:@"(?:[a-zA-Z0-9_]+)@(?:[a-zA-Z0-9])+\.com

8,非捕獲分組

"?:"此符號放在括號中代表非捕獲分組,意思是指定的括號不進行分組功能。


9,"正向預查"和"負正向預查"

在實際開發中比如我們有這樣的需求:在信息字符串"張三 性別:男 電話號碼:13699866695 李四 性別:女 電話號碼:13522693356 王五 性別:男 電話號碼:13859596459"中匹配出所有男性的姓名。

當然,這個需求我們用本篇前面的知識是做不到的,因爲前面的知識我們僅僅對匹配的字符串進行規則限定,而沒有提到對要匹配到的字符串的前面或者後面的字符進行限定。這就需要用到"預查"的概念。

正向預查是對要匹配到的字符串後面的字符或者字符串進行規則限定。

"?="正向預查:字符串後面的字符(串)進行限定。指定能夠出現的字符(串)。

"?!"負正向預查:對字符串後面的字符(串)進行限定,指定不能夠出現的字符(串)。

下面我們用"正向預查"來解決上面的問題:

//定義要被匹配的字符串

string content="張三 性別:男 電話號碼:13699866695 李四 性別:女 電話號碼:13522693356 王五 性別:男 電話號碼:13859596459";

//定義匹配規則,用正向預查指定要匹配的字符串後面的子串是"性別:男"。

Regex regex=new Regex(@"\b\w+\b(?=\s+性別:男)");

/*正則表達式說明:

*第一個\b指要匹配的字符(串)前面的空隙,第二個\b指要匹配的字符(串)後面的空隙。兩個\b分隔出一個單詞字符串\w+

*括號裏面的內容代表要匹配的字符串後面必須出現的字符串:\s+代表一個或者多個空字符,後面跟"性別:男"

*/

//匹配,並返回匹配結果集進行保存

MatchCollection matchs=regex.Matches(content);

//輸出所有匹配結果:

foreach(Match match in matchs)

{

Console.WriteLine(match);

}

//等待用戶輸出,結束程序

Console.ReadKey();

執行結果:


根據上面的例子不難理解正向預查的概念,那麼負正向預查也就自然而然的瞭解了,負正向預查就是限定匹配結果後面不能出現的字符串。


10,"反向預查"和"負反向預查"

上面的需求如果我們進行修改,修改成匹配出所有性別爲男的人的電話號碼,正向預查就解決不了這個問題了,因爲正向預查是對匹配內容之後的內容進行限定,而我們要對匹配結果之前的內容進行限定的話就不行了。此時我們就需要用到反向預查的概念。

"?<="反向預查,限定匹配結果之前的字符串,(指定匹配結果之前的字符串必須是哪些)。

"?<!"負反像預查,限定匹配結果之前的字符串,(指定匹配結果之前的字符串必須不是哪些)。

代碼:

//定義要被匹配的字符串

string content="張三 性別:男 電話號碼:13699866695 李四 性別:女 電話號碼:13522693356 王五 性別:男 電話號碼:13859596459";

//定義匹配規則

Regex regex=new Regex(@"(?<=性別:男\s+\b\w+\b:)\b\w+\b");

/*正則表達式說明:

*正則表達式小括號裏面的內容指定要匹配的字符串前面必須出現的字符串的正則規則

*"?<="是反向預查符號;"性別:男"是必須出現的字符串;性別男後面跟的"\s+"是"性別:男"後面跟的n(1個或多個)個空格;接着後面的兩個\b代表中間的單詞的字符分界,在匹配中也就是代表"電話號碼";接着後面跟的":"代表"電話號碼"後面的":"。

*小括號後面的\b\w+\b代表電話號碼字符串,其實更準確的應該用"\b\d+\b"因爲電話號碼肯定都是數字。

*/

//進行匹配並返回結果

MatchCollection matchs=regex.Matches(content);

//輸出所有返回結果

foreach(Match match in matchs)

{

Console.WriteLine(match);

}

//等到用戶輸入任何字符結束程序

Console.ReadKey();

執行結果:



四:正則表達式的相關方法和屬性。

1,Regex類的方法和屬性

Match()方法:匹配出符合匹配要求的第一個結果,並返回一個Match匹配結果對象。

Matches()方法:匹配出符合匹配要求的所有結果,形成一個結果集,並返回一個MatchCollection匹配結果集對象

IsMatch()方法:進行匹配並返回一個布爾型代表是否有匹配的結果,有則返回true,無則返回false。

以上三個方法的參數都是要被匹配的字符串,也可以是兩個參數,兩個參數的第一個參數依然是要匹配的字符串,第二個參數是匹配選項。(匹配選項是一個枚舉型,指定是否區分大小寫等。下面例舉一下匹配的選項。)

注意:以上方法都有對應的靜態方法,靜態方法在這裏就只說一個小例子,因爲靜態方法和動態的方法差不多,只是靜態方法是將要匹配的字符串作爲第一個參數傳入,第二個參數傳入參與匹配的正則表達式,第三個是可選參數,指定匹配模式。

靜態方法案例:使用正則表達式的靜態方法從字符串"你好,我是C#新手,我的姓名是張三"中匹配出姓名。

代碼:

//定義要匹配的字符串

string content="你好,我是C#新手,我的姓名是張三";

//使用靜態方法匹配並返回匹配的結果集

MatchCollection matchs=Regex.Matches(content,@"(?<=我的姓名是)\w+$");

//輸出匹配結果

foreach(Match match in matchs)

{

Console.WriteLine(match);

}

//等待用戶輸入結束程序

Console.ReadKey();

執行結果:


RegexOptions選項的枚舉的常用值:

RegexOptions.IgnorePatternWhitespace:消除匹配正則中的所有非轉義空白。

RegexOptions.IgnoreCase:指定該正則表達式不區分大小寫。

RegexOptions.Multiline:多行模式,使用該模式會改變^和$的含義,^和$將不再是匹配字符串的開頭和結尾,而是匹配每行的開頭和結尾,系統默認的是單行模式。

RegexOptions.RightToLeft:匹配從右到左進行,而不是從左到右進行。

Split()方法:匹配出對應的字符串,並用這些字符串進行分割,返回的是一個string類型的數組。


案例:將字符串"內容一(內容二%&內容三#內容四/內容五"分解成N多個子串,去掉所有的符號。

代碼:

//定義要分解的字符串

string content="內容一(內容二%&內容三#內容四/內容五";

//使用Split靜態方法分解,並返回字符串數組

string[] newContents=Regex.Split(content,@"[%&#/]+");

//輸出分解結果:

for(int i=0;i<newContents.Length;i++)

{

Console.WriteLine(newContents[i]);

}

//等待用戶輸入任意字符結束程序

Console.ReadKey();

執行結果:


Replace()方法:用指定的字符串替換字符串中符合匹配規則的子串,返回替換後的新字符串。

案例:將上例字符串中的"內容"替換爲"姓名"。使用正則表達式。這個例子用正則表達式的非靜態方法解決。
代碼:

//定義要被匹配的字符串

string content="內容一(內容二%&內容三#內容四/內容五";

//定義要匹配的正則

Regex regex=new Regex(@"內容");

//匹配內容,並進行替換,接收返回的替換後的字符串

string result=regex.Replace(content,"姓名");

//輸出匹配內容

Console.WriteLine(result);

//等待用戶輸入任何字符結束程序

Console.ReadKey();

執行結果:



MatchCollection類的常用屬性和方法

對於MatchCollection類的常用屬性和方法我們經常用到的只有Count屬性。返回MatchCollection集合的Match對象個數,該屬性常用於查詢符合要求的匹配的個數。


Match類的常用屬性和方法:

常用屬性:

Value屬性:匹配的的值。前面我們用Console.WriteLine(match);語句輸出的其實都是Value屬性的值。語句Console.WriteLine(match)等價於Console.WriteLine(match.Value)。

Groups屬性:該屬性是一個string類型的集合,保存匹配時候用小括號匹配的內容,用小括號匹配的內容用分組的形式儲存在了對應Match對象的Groups屬性中作爲一個元素儲存了。我們可以通過Match對象.Groups[下標]的形式訪問分組的數據。

Length屬性:該屬性對應匹配到的子串的長度。


常用方法:

Match類對象的常用方法只有一個:

NextMatch()方法。用來從前面的匹配的位置進行下一個匹配。

關於正則表達式筆者已經詳細介紹完了,相信從頭到尾閱讀了本篇文章的朋友能夠使用正則表達式了。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章