C#實現Regex入門實戰教程

來源:互聯網
上載者:User

C#實現Regex入門實戰教程

  如果有人和你說,如果不將字串轉換為數字,你要如何判斷字串是否由全數字組成?把字串拆成char數組,然後放入一個迴圈,來判斷每個char是否為數字?那你要如何判斷手機號是否合法?IP是否合法呢?把字串拆成char數組總不是個辦法啊,是否有更好的解決辦法?有的,Regex就是。Regex是什嗎?可以這麼說,它是一種字串文法,可以形容字串的格式。本文就來介紹Regex,就像我在其他部落格中講過的,我不喜歡把所有規則列出來,規則在網上一搜一大把。我要講的是Regex究竟能夠做什麼,怎麼使用Regex。

  維基百科中,Regex的解釋為:Regex(英語:Regular Expression、regex或regexp,縮寫為RE),也譯為正規標記法、常規標記法,在電腦科學中,是指一個用來描述或者匹配一系列符合某個句法規則的字串的單個字串。沒錯,Regex也是一個字串,只不過這個字串能夠用來判斷另一個字串是否滿足一定規則,如是否由全數字組成,是否是合法的手機號碼,是否是合法的IP。只要掌握了Regex,你就相當於掌握了一種描述Regex的文法,當然這種文法不是用來和人交流的,而是和”機器“交流的,你只要打出^((\w)(\w)\1\2)+$機器就能告訴你一個字串是否長成ababab...這樣。下面我來講解Regex。我不會列出所有文法,因為網上很多。

  我會以Lesson的形式,先介紹執行個體,然後詳細分析執行個體,因為Regex的規則確實很難記,但是能達到的效果是很好記的,只要你看過有人實現了利用Regex來判斷一個字串是否由全數字組成,那麼你就再也不會忘了,下次再有類似的要求的時候,你至少知道用Regex來做會很簡單,那麼剩下的就是翻出Regex的手冊,研究一陣子,自然就出來了,因為雖然規則難記,但是很好理解。 

  • Lesson1 判斷字串是否由全字元組成。

  該題的想法很簡單,從”頭“到”尾“,全是數字就可以了。開啟手冊,看到表示開頭的字元是^,表示結尾的字元是$,表示數位字元是\d,然後看到想要匹配多個字元,有兩個選擇,+和*,+是至少出現一次,*是0次或者多次,^\d+$。這就完成了!好,讓我們來試一試。在c#中,Regex相關的類是System.Text.RegularExpressions.Regex,其中我用到的方法有Match,Replace,和IsMatch,分別是匹配,替換和判斷是否匹配的便捷方法。用法也很簡單,看下我的例子就會明白了。

internal static class RegexExtension
{//將匹配資訊轉換為字串資訊
    public static string Convert2String(this Match match, string enter)
    {
        StringBuilder builder = new StringBuilder();
        builder.AppendFormat("匹配的字串為:{0}, 是否匹配成功{1}, 匹配到的字串為{2}, 匹配的位置為{3}, 匹配的長度為{4},一共匹配到{5}個結果",
            enter, match.Success, match.Value, match.Index, match.Length, match.Groups.Count);
        return builder.ToString();
    }
}

public static void Lesson1(){
    Console.WriteLine("全數位判斷:");
    string[] enters = {"123123123", "123a123", "a123123", "", "0 "};
    foreach (var enter in enters){
        Console.WriteLine(
            Regex.Match(enter, @"^\d+$").Convert2String(enter));
    }
}

  Regex.Match方法會將enter與規則進行匹配,然後將結果轉換為字串,例子中只有 ”123123123“ 會匹配成功,其他都是失敗,即使”0 “也會失敗,是因為有空格,而\d只會匹配數字。是不是很簡單?下面來看第二課。

  • Lesson2 判斷號碼是否為合法的有線電話或者手機號碼

   判斷號碼是否為固話或者手機號,固話是7-8位,手機為11位,這時就需要2種情況都可以匹配,先看固話,全是數字已經會了,是^\d+$ ,只要加上個數的限制就可以了,查看手冊,發現{m,n} 描述有幾個字元,m表示最少出現的次數,n表示最多出現的次數。那麼 +字元就應該是和{1,} 一樣,不填n表示不限制最大次數,那麼m不填就表示不限制最小次數。然後是手機,手機為11位,且由1開頭,且都為數字,那麼就很簡單了:^1\d{10}$。如何把他們拼起來?字元 | 可以辦到,下面是代碼。

public static void Lesson2(){
    Console.WriteLine("是否為合法的電話號碼,電話號碼的規則分兩部分:");
    Console.WriteLine("固話為7-8位,手機為11位,且由1開頭,且都為數字。");
    string landPhoneRule = @"^\d{7,8}$";
    string handPhoneRule = @"^1\d{10}$";
    //規則合并
    string rule = string.Format("{0}|{1}", landPhoneRule, handPhoneRule);
    string[] enters ={
        "1234567", //7位元字,合法
        "12345678", //8位元字,合法
        "13888888888", //11位以1開頭的數字,合法
        "23888888888", //11位以2開頭的數字,非法
        "0123456789", //10位元字,非法
        "1388888888a",//帶有字元,非法
        "10111111111"//11數字,合法
    };
    foreach (var enter in enters){
        Console.WriteLine(Regex.Match(enter, rule));
    }

    Console.WriteLine("現改變手機的規則,改為:手機要以數字1開頭,且第二位和第三位不能有數字0,其他不變");
    handPhoneRule = @"^1[1-9]{2}\d{8}$";
    rule = string.Format("{0}|{1}", landPhoneRule, handPhoneRule);
    foreach (var enter in enters){
        Console.WriteLine(Regex.Match(enter, rule));
    }
}

   我們看到10111111111也能匹配,顯然這不是個手機號,因此在後面我更改了規則,添加了第二位和第三位不是0的限制。這時 \d 不滿足條件了,查看手冊,發現 [] 字元,可以在裡面添加候選字元,例如 [123] 指匹配123,也可以用 - 來添加範圍,如 [0-9] 和 \d 是一樣的。那麼更改後的手機號碼匹配的規則就變成了 ^1[1-9]{2}\d{8}$ 。下面是第三課。

  • Lesson3 判斷IP是否合法

  這一題,我將IP的判斷稍微簡化了些,規則是必須是 ***.***.***.*** ,其中每項的字元數最少為1位,最多為3位,且 255>=*** >= 0, 第一項不能為0\00\000。這一題的判斷要複雜的多,您也能看到Regex的一個”短板“,那就是不能擷取字元的意義,後面我會解釋這一點。看條件,基本的文法我前面都介紹了,該規則可以拆分為兩部分,第一項和後三項。第一項,255>=*** >= 0, 且不能為0\00\000。我在思考這道題的時候,先按照正向的思路想,即”描述什麼樣的滿足條件“,情況非常多,01,001,011,1-249,250-255,一共這麼多種情況,Regex是沒有辦法”忽略“的,如果是數字, if (001 == 1)是成立的,但是Regex辦不到,你只能 (0[2]1)|1 來描述1和001都滿足條件,這就是我前面說的,無法擷取字元本身的意義,你只能每一位的描述一個字串,第一位是什麼,第二位不是什麼,而無法通過意義來形容字串。我通過”正向”的列舉每種情況,運算式是這樣的:(0{2}[1-9]|0[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|0[1-9]|[1-9]\d|[1-9]) 。其意義大概就是把01,001,011,1-249,250-255的全部情況都列舉了出來,這麼做沒什麼“錯”,但是太長了,有沒有什麼簡便做法?“正向”思考不行,那麼我們來“反向”試一下:第一項是1-3個數字,不能全是0,不能大於255,也就是不能是 ^0{1,3},2[6-9]\d,25[6-9],[3-9]\d{2},只要不是前面的條件,就可以。如何形容不滿足的條件? [^]可以,但是只能指明一個字元。可以看到手冊的下面有幾個?<!這樣的字元,他們表示“附近是否滿足條件”,舉個例子:?! 加一起表示正向否定的預尋找, Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”,還有其他的。這裡我用?<! ,表示反向否定尋找,即(?<!123)456表示能匹配23456,但是不能匹配前面帶123的456,如123456就不滿足條件。這個正向和反向就是前面和後面,肯定就是匹配,否定就是排除。我的第一項的運算式是:^(\d{1,3})(?<!^0{1,3}|2[6-9]\d|25[6-9]|[3-9]\d{2}),第二項的前面如果是上述幾種情況,則第二項永遠不會匹配。接下來是後面三項,後面的三項都是 .***,255>=***>=0,語句是(.[01]?\d?\d|2[0-4]\d|25[0-5]){3}$,加起來就是 ^(\d{1,3})(?<!^(0{1,3}|2[6-9]\d|25[6-9]|[3-9]\d{2}))(.[01]?\d?\d|2[0-4]\d|25[0-5]){3}$ 。代碼如下:

public static void Lesson3(){
    Console.WriteLine("題目是判斷一個IP是否合法,規則如下:");
    Console.WriteLine("格式必須是***.***.***.***");
    Console.WriteLine("其中第一組數字必須大於1, 每組數字都要小於等於255");
    //複雜版    string rule =  @"^(\d{1,3})(?<!^(0{1,3}|2[6-9]\d|25[6-9]|[3-9]\d{2}))(.[01]?\d?\d|2[0-4]\d|25[0-5]){3}$";
    string[] enters =
    {
        "255.255.255.255", //合法        "21.1.1.1", //合法        "256.0.0.0", //非法
        "300.2.2.250", //非法        "10.1.1.99", //合法        "00.1.1.009",//非法
        "100.1.1.1"//合法    };
    foreach (var enter in enters){
        Console.WriteLine(Regex.Match(enter, rule).Convert2String(enter));
    }
}

  這裡要吐槽下,如果能轉成數字,判斷IP是否合法,只要把數字截出來,判斷 >=0&& <=255就可以了,這裡也可能有更簡便的做法,筆者一時沒想出來,歡迎有好辦法的同學留言給我。

  • Lesson4 函數替換

  這個題目是我學習Regex的起源。當時我在重構代碼,發現有一個方法寫的多餘我們管這個多餘的方法叫MA,應該換成MB(另一個方法),MA接受2個變數,MB接受一個變數。VS提供了自動更換函數名字的功能,但是對於函數變數的變化就無能為力了。當時全工程有接近50個MA的調用,都要替換成MB,如果手動一下一下的刪除多餘的參數,那實在是太噁心了。VS尋找介面提供了替換功能,我看到搜尋選項裡面有Regex一項,是否能夠使用Regex來自動替換?於是就開始研究起Regex,是1天還是2天,磕磕絆絆,研究出了運算式,然後一試,確實好使!怕匹配錯,就點擊替換,一個一個的換,中間多次調整運算式,最終在沒有手動更改的情況下,全部替換成功,當時真的很高興,感覺Regex很神奇。因此我就把這個情景轉換為第四課的內容,前面講的都是匹配,這一課就來看看如何進行替換。下面是題目的要求,上面的一組Console.WriteLine是原語句,下面的4個Console.WriteLine是替換後的樣子。就是用MB替換MA,並保留MA的第一個參數給MB,捨棄第二個參數。每條語句都毫無意義,只是用來替換。

//下面的語句沒有任何的實際意義,只是類比 想要替換的語句的具體使用。Console.WriteLine(MA("a", "b")),
Console.WriteLine("a" + MA("a", GetType().ToString()));
Console.WriteLine(MA("a", GetType().ToString()));
Console.WriteLine("a" + MA("a", "a".Substring(1)) + "b");

//替換成如下的樣子Console.WriteLine(MB("a"));
Console.WriteLine("a" + MB("a"));
Console.WriteLine(MB("a"));
Console.WriteLine("a" + MB("a") + "b");
//這兩個方法沒有任何意義,只是用MA類比原函數,MB類比想要被替換的函數public string MA(string a, string b) { return null; }
public string MB(string a) { return null; }

  先來匹配,分析一下要替換的4條語句:開始的部分都是函數名MA,內容是()內的部分,只要找到適當的‘(’和‘)’,就可以了。但事實是無法確定到底哪個‘)’才是合適的,能看到後的語句後面有一個‘)’,有的是兩個,有的是三個。這裡括弧要精確匹配,不能匹配多了,不然語句就錯誤了。一個‘)’的情況是帶個",其他的至少有2個‘)’,因此我們可以分兩種情況,第一種是"),第二種是多個括弧的前兩個。這裡有一個問題,如何只匹配前兩個?在Regex中,有兩種匹配方式,一種是貪心,一種是非貪心。貪心的意思是能匹配幾個就匹配幾個,比如a+,在匹配aaaaaab的時候,會匹配全部的a字母,結果是aaaaaa。而如果加入a+?,?的本來意思是0個或1個,但是在這裡,表示最多匹配1個,結果就是a。還有一個問題就是,Regex中,()是有特殊意義的,如果放棄其特殊意義,只是想匹配括弧,就要用轉義副\,這個用過ASC||碼的都應該能明白。則匹配MA函數的運算式為:MA\((.+),\s?.+?(\)\))|""\)  。注意,c#中雙引號要這樣 @"""",前面用@,然後用兩個""表示一個雙引號。讀別人的Regex會有些費勁,那麼我的建議是你可以先不看我的結果,只要明白題意,一邊查看手冊,一邊自己練習一點一點的試,很快就能做出來,這時 你就明白大概了,再來看拿自己的運算式與別人的對比,很有可能你做的比我簡略!

  可以看到在\s?之後,我用.+?來盡量少的匹配字元,直到遇到))或者")就停止匹配,如果只用.+,就會過多的匹配括弧。 光有Regex還不夠,還要能替換。替換的痛點在如何保留第一個變數。Regex提供了提取括弧裡匹配到則值得機制,如(\w)\1(\w)\2,能夠匹配aabb,第一個\w匹配到了a,由於在括弧中,因此被記錄了下來,可通過\1來獲得第一個括弧中的內容,以此類推。在替換時,C#(其他語言不清楚),可利用$1來獲得第一個括弧中匹配到的內容。則替換的語句是MB($1)。注意,這裡替換的語句不需要轉義,因為替換不需要匹配,只要將字元原封不動替換就可以了。下面是代碼:

public static void Lesson4(){
    Console.WriteLine("第四課,替換函數。");
    string rule = @"MA\((.+?),\s?.+?(\)\)|""\))";
    string[] enters =
    {
        @"Console.WriteLine(MA(""a"", ""b""))",
        @"Console.WriteLine(""a"" + MA(""a"", GetType().ToString()));",
        @"Console.WriteLine(MA(""a"", GetType().ToString()));",
        @"Console.WriteLine(""a"" + MA(""a"", ""a"".Substring(1)) + ""b"");"
    };
    string replacement = @"MB($1)";
    foreach (var enter in enters)
    {
        Console.WriteLine(Regex.Match(enter, rule).Convert2String(enter));
        Console.WriteLine("替換後由{0}變為{1}", enter, Regex.Replace(enter, rule, replacement));
    }
}

  Regex看起來很難,但如果您把這4課都做一遍,就已經基本掌握了Regex了,日常的應用是沒有問題的。文法有些難記,我的建議是先不管文法,要用就隨時翻手冊。歡迎各位同學在評論區與我互動。

本文永久更新連結地址:https://www.bkjia.com/Linux/2018-03/151495.htm

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.