正規表示式的學習

mymdeep發表於2017-05-11

以前在程式中登陸或者校驗也會經常用正則,但是一般都是去網上找現成的,最近研究了一下,還是發現很博大精深的,而且,熟練了語法,的確非常方便,所以在這裡總結推薦給大家。

為什麼使用正規表示式

在工作中經常會遇到這樣的情況,,比如我們需要驗證輸入是不是一個座機電話,如010-1234567.如果不用正規表示式,怎麼判斷呢?
看一下下面這個程式碼:

 private boolean isTel(String input){
        if (input.length()!=11){
            return false;
        }
        for (int i = 0; i<input.length();i++){
            if (i==3){
                if (input.charAt(i)!='-'){
                    return false;
                }
            }else {
                if (!Character.isDigit(input.charAt(i))){
                    return false;
                }
            }

        }
        return true;
    }複製程式碼

感覺有些麻煩,如果還要判定必須是010開頭的或者01x開頭的,豈不是更麻煩,這樣,使用正規表示式,就可以解決這個問題。不僅僅是判斷電話,包括驗證郵箱,驗證網址,找到一個長字串中的固定字母位置等等,使用正規表示式都是很方便的。

用法

在介紹用法之前,需要知道正規表示式中的一些特殊符號代表什麼,這個可以查詢文章結尾的附錄。

驗證座機電話

既然說到了是驗證電話,那就先看看,座機號碼如何驗證:

Pattern pattern;
Matcher matcher;
 pattern = Pattern.compile("\\d\\d\\d[-]\\d\\d\\d\\d\\d\\d\\d");
                 matcher = pattern.matcher(result);

                if (matcher.matches()){
                   show(matcher.group());
                }else {
                    show("不是座機號碼");
                }複製程式碼

其中result是輸入的字串。
\d代表的是數字,\d前面還有一個\\是為了轉義。
[]中的字元表示的是固定字元,在電話中固定字元就是-。
matcher.group()是將匹配結果輸出

手機號的驗證

  pattern = Pattern.compile("[1][3|7|8]\\d{9}");
                 matcher = pattern.matcher(result);

                if (matcher.find()){
                    show(matcher.group());
                }else {
                    show("不是手機號碼");
                }複製程式碼

第一個[1]表示第一位是1,[3|7|8]表示第二位可以使3,7,8,\d{9}表示後面九位都是數字型別,所以用\d

郵箱驗證

 String regEx = "[a-zA-Z_]{1,}[a-zA-Z_0-9]{0,}@(([a-zA-Z0-9]){1,}\.){1,3}[a-zA-Z\-]{1,}";
                pattern = Pattern.compile(regEx);
                matcher = pattern.matcher(result);

                if (matcher.find()){
                    show(matcher.group());
                }else {
                    show("不是郵箱");
                }複製程式碼

[a-zA-Z]表示英文字母和下劃線,任意都可以。其中a-z這種寫法,表示a-z任何字母,如果x-z就只能匹配xyz。
{1,}表示[a-zA-Z
]中所匹配的字母至少出現1次。連起來就表示,字串要以字母或下劃線開始,開始的時候必須有1位字母哪怕f@163.com都是可以的。
字母之後是數字字母下劃線都可以了{0,}表示至少有0位,然後就是@符號。
(([a-zA-Z0-9]){1,}.)表示以任何字元開始,至少有1個,然後以.結尾的字串,即xxx.型別的字串,{1,3}至少有一個,最多有3個,為的是有些郵箱是@vip.163.xxx,這樣的就會有兩個xxx.
最後以[a-zA-Z-]{1,}結尾,表示最後的.後面還要接一個字串,可能是com可能是net

部分匹配與嚴格匹配

好吧,我不知道該如何形容這個概念,我在學習這塊的時候,一直不明白的一個問題,就是正規表示式,可以校驗一個字串,但是有兩種情況:
1.比如我希望字串中有手機號,然後我用正則可以取到字串那麼這個字串是“手機號:136xxxxxxxx”都是可以的,也就是,前面有部分中文,是不影響的,我只取出我要的。
2.比如我希望嚴格匹配,也就是“手機號:136xxxxxxxx”是不行的,必須要“136xxxxxxxx”不能帶有其他字元。
這種情況我的想法是依靠^和$。

  • ^在[^a-z]裡面表示取反,也就是匹配不再a-z中的符號字母,但是如果放在開頭可以標識以xxx開頭。
  • $表示以xxx結尾
    還是以上面的手機號的做為例子,我們的正規表示式是"[1][3|7|8]\\d{9}"那麼我們匹配"tel is 136xxxxxxxx"也是可以匹配到的,現在我們改一下匹配規則:"^[1][3|7|8]\\d{9}$",這時就不能再匹配"tel is 136xxxxxxxx"了,只能匹配"136xxxxxxxx"

分割

 pattern = Pattern.compile("[,|]+");
                String[] strs = pattern.split("hello,Hello android,hello deep,,android|Google");
                for (int i=0;i<strs.length;i++) {
                    Log.e("deep","strs="+strs[i]);
                }複製程式碼

輸出結果是:

05-11 11:49:30.473 30824 30824 E deep    : strs=hello
05-11 11:49:30.473 30824 30824 E deep    : strs=Hello android
05-11 11:49:30.473 30824 30824 E deep    : strs=hello deep
05-11 11:49:30.475 30824 30824 E deep    : strs=android
05-11 11:49:30.475 30824 30824 E deep    : strs=Google複製程式碼

"[,|]+"中+是以前面的字元至少一個進行匹配,[,|]是以,或|進行匹配,所以匹配到字串中就是,或,,或|。
由於用的是split方法,所以就是以匹配的字元進行分割,分割結果如log所示

替換

單個替換

pattern = Pattern.compile("android");
                 matcher = pattern.matcher("android Hello World,android Hello World");
                Log.e("deep",matcher.replaceFirst("deep"));複製程式碼

結果如下:

05-11 13:36:03.285 23098 23098 E deep    : deep Hello World,android Hello World複製程式碼

matcher.replaceFirst只替換了匹配到的第一個

全部替換

 pattern = Pattern.compile("android");
                 matcher = pattern.matcher("android Hello World,android Hello World");
                Log.e("deep",matcher.replaceAll("deep"));複製程式碼

結果如下:

05-11 13:38:09.739 24160 24160 E deep    : deep Hello World,deep Hello World複製程式碼

matcher.replaceAll替換了匹配到的所有android

appendReplacement

pattern = Pattern.compile("android");
                 matcher = pattern.matcher("android Hello World,android Hello World ");
                StringBuffer sbr = new StringBuffer();
                while (matcher.find()) {
                    matcher.appendReplacement(sbr, "deep");
                }
                matcher.appendTail(sbr);
                Log.e("deep",sbr.toString());複製程式碼

結果如下:

05-11 13:38:09.739 24160 24160 E deep    : deep Hello World,deep Hello World複製程式碼

appendReplacement是逐段匹配替換新增到緩衝區

附錄

附錄中內容摘自網路不同文章,我只是把他們貼上在了一起。

語法

語法 用處
\ 轉移符:將下一個字元標記符、或一個向後引用、或一個八進位制轉義符。例如,“\n”匹配\n。“\n”匹配換行符。序列“\”匹配“\”而“(”則匹配“(”。
^ 匹配輸入字串的開始位置。如果設定了RegExp物件的Multiline屬性,^也匹配“\n”或“\r”之後的位置。
$ 匹配輸入字串的結束位置。如果設定了RegExp物件的Multiline屬性,^也匹配“\n”或“\r”之後的位置。
* 匹配前面的子表示式任意次。例如,zo能匹配“z”,“zo”以及“zoo”。等價於{0,}。
+ 匹配前面的子表示式一次或多次(大於等於1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等價於{1,}。
? 匹配前面的子表示式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等價於{0,1}。
{n} n是一個非負整數。匹配確定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的兩個o。
{n,} n是一個非負整數。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等價於“o+”。“o{0,}”則等價於“o*”。
{n,m} m和n均為非負整數,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”將匹配“fooooood”中的前三個o。“o{0,1}”等價於“o?”。請注意在逗號和兩個數之間不能有空格。
? 當該字元緊跟在任何一個其他限制符(*,+,?,{n},{n,},{n,m})後面時,匹配模式是非貪婪的。非貪婪模式儘可能少的匹配所搜尋的字串,而預設的貪婪模式則儘可能多的匹配所搜尋的字串。例如,對於字串“oooo”,“o+?”將匹配單個“o”,而“o+”將匹配所有“o”。
.點 匹配除“\r\n”之外的任何單個字元。要匹配包括“\r\n”在內的任何字元,請使用像“[\s\S]”的模式。
^[\u0391-\uFFE5]+$ 一個或多個漢字
x(豎線)y 匹配x或y。例如,“z(豎線)food”能匹配“z”或“food”或"zood"(此處請謹慎)。“(z(豎線)f)ood”則匹配“zood”或“food”。
[xyz] 字符集合。匹配所包含的任意一個字元。例如,“[abc]”可以匹配“plain”中的“a”。
[\^xyz] 負值字符集合。匹配未包含的任意字元。例如,“[^abc]”可以匹配“plain”中的“plin”。
[a-z] 字元範圍。匹配指定範圍內的任意字元。例如,“[a-z]”可以匹配“a”到“z”範圍內的任意小寫字母字元。注意:只有連字元在字元組內部時,並且出現在兩個字元之間時,才能表示字元的範圍; 如果出字元組的開頭,則只能表示連字元本身.
[\^a-z] 負值字元範圍。匹配任何不在指定範圍內的任意字元。例如,“[\^a-z]”可以匹配任何不在“a”到“z”範圍內的任意字元。
\b 匹配一個單詞邊界,也就是指單詞和空格間的位置(即正規表示式的“匹配”有兩種概念,一種是匹配字元,一種是匹配位置,這裡的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
\B 匹配非單詞邊界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\cx 匹配由x指明的控制字元。例如,\cM匹配一個Control-M或回車符。x的值必須為A-Z或a-z之一。否則,將c視為一個原義的“c”字元。
\d 匹配一個數字字元。等價於[0-9]。
\D 匹配一個非數字字元。等價於[^0-9]。
\f 匹配一個換頁符。等價於\x0c和\cL。
\n 匹配一個換行符。等價於\x0a和\cJ。
\r 匹配一個回車符。等價於\x0d和\cM。
\s 匹配任何不可見字元,包括空格、製表符、換頁符等等。等價於[ \f\n\r\t\v]。
\S 匹配任何可見字元。等價於[^ \f\n\r\t\v]。
\t 匹配一個製表符。等價於\x09和\cI。
\v 匹配一個垂直製表符。等價於\x0b和\cK。
\w 匹配包括下劃線的任何單詞字元。類似但不等價於“[A-Za-z0-9_]”,這裡的"單詞"字元使用Unicode字符集。
\W 匹配任何非單詞字元。等價於“[^A-Za-z0-9_]”。
\xn 匹配n,其中n為十六進位制轉義值。十六進位制轉義值必須為確定的兩個數字長。例如,“\x41”匹配“A”。“\x041”則等價於“\x04&1”。正規表示式中可以使用ASCII編碼。
\num 匹配num,其中num是一個正整數。對所獲取的匹配的引用。例如,“(.)\1”匹配兩個連續的相同字元。
\n 標識一個八進位制轉義值或一個向後引用。如果\n之前至少n個獲取的子表示式,則n為向後引用。否則,如果n為八進位制數字(0-7),則n為一個八進位制轉義值。
\nm 標識一個八進位制轉義值或一個向後引用。如果\nm之前至少有nm個獲得子表示式,則nm為向後引用。如果\nm之前至少有n個獲取,則n為一個後跟文字m的向後引用。如果前面的條件都不滿足,若n和m均為八進位制數字(0-7),則\nm將匹配八進位制轉義值nm。
\nml 如果n為八進位制數字(0-7),且m和l均為八進位制數字(0-7),則匹配八進位制轉義值nml。
\un 匹配n,其中n是一個用四個十六進位制數字表示的Unicode字元。例如,\u00A9匹配版權符號(\©)。
\< \> 匹配詞(word)的開始(\<)和結束(\>)。例如正規表示式\能夠匹配字串"for the wise"中的"the",但是不能匹配字串"otherwise"中的"the"。注意:這個元字元不是所有的軟體都支援的。
+ 匹配1或多個正好在它之前的那個字元。例如正規表示式9+匹配9、99、999等。注意:這個元字元不是所有的軟體都支援的。
? 匹配0或1個正好在它之前的那個字元。注意:這個元字元不是所有的軟體都支援的。
{i} {i,j} 匹配指定數目的字元,這些字元是在它之前的表示式定義的。例如正規表示式A[0-9]{3} 能夠匹配字元"A"後面跟著正好3個數字字元的串,例如A123、A348等,但是不匹配A1234。而正規表示式[0-9]{4,6} 匹配連續的任意4個、5個或者6個數字

flag的取值範圍

這裡不再詳細描述了,因為我還沒有遇到過需要設定這些flag的情況,但是還是在這裡總結給大家。
在使用Pattern.compile函式時,可以加入控制正規表示式的匹配行為的引數:
Pattern Pattern.compile(String regex, int flag)

  • Pattern.CANON_EQ 當且僅當兩個字元的"正規分解(canonical decomposition)"都完全相同的情況下,才認定匹配。比如用了這個標誌之後,表示式"a/u030A"會匹配"?"。預設情況下,不考慮"規 範相等性(canonical equivalence)"。
  • Pattern.CASE_INSENSITIVE 預設情況下,大小寫不明感的匹配只適用於US-ASCII字符集。這個標誌能讓表示式忽略大小寫進行匹配。要想對Unicode字元進行大小不明感的匹 配,只要將UNICODE_CASE與這個標誌合起來就行了。
    Pattern.COMMENTS 在這種模式下,匹配時會忽略(正規表示式裡的)空格字元(譯者注:不是指表示式裡的"//s",而是指表示式裡的空格,tab,回車之類)。註釋從#開始,一直到這行結束。可以通過嵌入式的標誌來啟用Unix行模式。
  • Pattern.DOTALL 在這種模式下,表示式'.'可以匹配任意字元,包括表示一行的結束符。預設情況下,表示式'.'不匹配行的結束符。
  • Pattern.MULTILINE
    在這種模式下,'^'和'$'分別匹配一行的開始和結束。此外,'^'仍然匹配字串的開始,'$'也匹配字串的結束。預設情況下,這兩個表示式僅僅匹配字串的開始和結束。
  • Pattern.UNICODE_CASE
    在這個模式下,如果你還啟用了CASE_INSENSITIVE標誌,那麼它會對Unicode字元進行大小寫不明感的匹配。預設情況下,大小寫不敏感的匹配只適用於US-ASCII字符集。
  • Pattern.UNIX_LINES 在這個模式下,只有'/n'才被認作一行的中止,並且與'.','^',以及'$'進行匹配。

有疑問的朋友歡迎給我留言指正,或者關注我的公眾號留言:

正規表示式的學習

相關文章