天天看點

Java之正規表達式

        正規表達式約定特殊的字元和規則,使用一串特殊字元的排清單示出某一字元串的特征,是用于進行文本比對的工具。

一、java.util.regex包中主要包含三個類:

        1、Pattern類:正規表達式的編譯表示形式。常用方法有:

                1>static Pattern compile(String regex):将指定的正規表達式編譯到模式中。

Pattern p = Pattern.compile("a*b");
           

                2>Matcher matcher(String str):建立比對指定字元串與本模式的比對器。

Pattern p = Pattern.compile("a*b");
Matcher m = p.matcher("aaaaaaaaab");
           

                3>static boolean matches(String regex,String str):編譯指定的正規表達式并與指定的字元串進行比對。

Pattern.matches("a*b", "aaaaaaaaab");
           

                4>String[] split(String str,int limit):使用編譯到模式中的正規表達式分隔指定的字元串指定的次數。如果為負數或0次,則分隔次數不作限制。如果為0時丢棄尾部的空字元串。

Pattern p = Pattern.compile("\\|");
String[] strs = p.split("a|b|c|d",-3);
           

        2、Matcher類:對輸入字元串進行解釋和比對操作的引擎。需要調用Pattern對象的matcher方法來獲得一個 Matcher對象。其有3種比對方式:

                1>matches():整個比對,隻有整個字元序列完全比對成功,才傳回true,否則傳回false。如果本部分比對成功,将移動到下次比對的位置。

                2>lookingAt():部分比對,總是從第一個字元進行比對,不管比對成功與否都不再繼續比對。

                3>find():部分比對,從目前位置開始比對,找到一個比對的子串,将移動下次比對的位置。

        除了3種比對方式外,常用的方法還有:

                4>int start():傳回第一次比對字元之後的偏移量,也就是說調用此方法之前必須先調用比對方法。

                5>int end():傳回最後比對字元之後的偏移量,也就是說調用此方法之前必須先調用比對方法。

                6>String group(int i):傳回由之前比對操作所比對的指定捕獲組編号的子序列,也就是說調用此方法之前必須先調用比對方法。

                7>Matcher reset():重置比對器。

                8>String replaceAll(String replacement):将與給定正規表達式相比對的每個子序列替換為指定的值。

        3、PatternSyntaxException類:抛出未經檢查的異常,表明正規表達式模式中的文法錯誤。

二、文法及規則約定:

        1、表示字元的方式:

                1>轉義字元:在正規表達式中,有些字元表示特殊的含義,如果需要比對這些字元本身,則不能再使用這些字元,而必須通過處理才能使用,否則它将是正規表達式中表示的特珠含義。

字元 正規表達式中的含義 處理方式
^ 單獨使用時表示比對輸入字元串的開始位置,如果放在[]中表示否定 \\^
$ 比對輸入字元串的結尾位置,如果設定了RegExp對象的Multiline屬性,則$也比對‘\n'或‘\r' \\$
() 表示捕獲組的開始和結束 \\( \\)
[] []中指定的多個字元中的任意一個,或者指定一個範圍 \\[ \\]
{} 限定某部分出現的次數 \\{ \\}
. 除換行符以外的任意一個字元 \\.
\ 用在轉義字元前可比對特殊字元,用在捕獲組編号前表示之前所比對的指定編号的組,用在0xxx之前表示八進制表示的字元。由于"\"比較特殊,在String中也需要轉義處理,是以String中的\是"\\",而正則比對時則需要寫成\\\\ \\\\
| 或者,兩項之間的任一個 \\|
? 跟在字元後表示該字元不出現或出現1次,跟在數量限定符後表示非貪婪限制符 \\?
+ 出現1次或多次 \\+
* 不出現或出現多次 \\*

                2>預定義字元:使用正規表達式中已事先定義的字元表示特定的值:

字元 表示含義 等價表示
\\d 數字 [0-9]
\\D 非數字 [^0-9]
\\s 空白字元
\\S 非空白字元 [^\\s]
\\w 單詞字元 [a-zA-Z_0-9]
\\W 非單詞字元 [^\\w]

                3>字元縮略表示:有些字元比較抽象,比如換行符等,可以用特殊的字元或者Unicode轉義表示:

含義 表示 Unicode轉義表示
制表符 \t \u0009
換行符 \n \u000A
回車符 \r \u000D
換頁符 \f \u000C
報警符 \a \u0007
轉義符 \e \u001B
垂直制表符 \v \u000B

                4>進制表示:使用八進制或十六進制表示字元:

                ①八進制表示:格式可以是\0n,\0nn或者\0mnn,其中0<=n<=7,0<=m<=3。

                ②十六進制表示:格式隻能是\xhh,其中h隻能在0-9、a-f或者A-F之間。

                5>Unicode轉義表示:格式是\uhhhh,其中h隻能在0-9、a-f或者A-F之間。

                6>指定比對某個固定字元:直接将該字元放入正規表達式即可。

boolean b = Pattern.matches("A","AB");
           

                7>指定比對某幾個字元裡的任意一個:

                ①使用中括号[]将可選的字元包覆即可。

boolean b = Pattern.matches("[ABC]","A");
           

                ②使用|

boolean b = Pattern.matches("A|B","A");
           

                8>指定比對某個連續範圍的某個字元:在[]中用-指定一個範圍

boolean b = Pattern.matches("[A-Za-z]","A");
           

                9>指定比對某個範圍的字元并且比對另一個範圍的字元:兩個範圍使用&&連接配接并放在[]中

boolean b = Pattern.matches("[[a-z]&&[abcde]]","a");
           

                10>指定比對不是某幾個字元:在不是的字元前加^并放在[]中

boolean b = Pattern.matches("[^a-c]", "b");
           

        2、表示字元出現的次數:

                1>字元不出現或出現1次:?

boolean b = Pattern.matches("ab?c", "ac");
           

                2>字元出現1次或多次:+

boolean b = Pattern.matches("ab+c", "abbbc");
           

                3>字元出現0次或多次:*

boolean b = Pattern.matches("ab*c", "ac");
           

                4>字元出現指定的次數:使用{}将次數括起來,比如字元出現3次。

boolean b = Pattern.matches("ab{3}c", "abbbc");
           

                5>字元出現的次數是一個範圍:使用{}将次數括起來,最小值和最大值用,隔開,比如字元最少出現3次,最多出現7次

boolean b = Pattern.matches("ab{3,7}c", "abbbbbbbc");
           

                6>字元出現最少的指定次數:使用{}将最少次數括起來,去掉最大次數即可。比如字元最少出現3次

boolean b = Pattern.matches("ab{3,}c", "abbbc");
           

三、捕獲組:

        捕獲組就是把正規表達式中子表達式比對的内容,儲存到以數字編号的記憶體中或儲存到顯式命名的組裡,友善後面引用。這種引用既可以是在正規表達式内部,也可以是在正規表達式外部。捕獲組用括号表示。

        1、編号規則:按照“(”出現的順序,從左到右,從1開始進行編号的。特殊地,編号為0的捕獲組,指的是正規表達式整體。

Java之正規表達式

        2、命名捕獲組的命名規則:通過顯式命名,可以通過組名友善的通路到指定的組,而不需要去一個個的數編号。同時避免了在正規表達式擴充過程中,捕獲組的增加或減少對捕獲組編号的變化導緻的不可控情況。給捕獲組命名的方式是:在要命名的組所在的括号中将”?<别名>“放在該組的正規表達式之前,此時<>中的名字即為該組的别名。比如給一個比對格式為yyyy-MM-dd的日期的正規表達式進行捕獲組命名,此時别名分别是year、date、month和day:

(?<year>\\d{4})-(?<date>(?<month>\\d{2})-(?<day>\\d\\d))
           
Java之正規表達式

        3、在正規表達式内部使用捕獲組(即反向引用):在要使用的捕獲組之後使用\\加捕獲組編号或者\\k<捕獲組名>,即可将捕獲組比對的字元串作為正規表達式的一部分使用。比如:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\3");
Matcher m = p.matcher("2017-03-31-04");
System.out.println(m.matches());
           

或者:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\k<month>");
Matcher m = p.matcher("2017-03-31-04");
System.out.println(m.matches());
           

的結果傳回false,因為正規表達式的最後部分使用了反向引用,即需要比對之前比對到的捕獲組編号是3(别名是month)的内容(即字元串“2017-03-31-04”中的03)。是以:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\3");
Matcher m = p.matcher("2017-03-31-03");
System.out.println(m.matches());
           

或者:

Pattern p = Pattern.compile("(\\d{4})-((?<month>\\d{2})-(?<day>\\d\\d))-\\k<month>");
Matcher m = p.matcher("2017-03-31-03");
System.out.println(m.matches());
           

才會傳回true。

四、比對模式:當在字元後跟數量限定時(比如字元後跟?、+、*或者{2,5}),會涉及到比對模式的問題。

        1、貪婪模式(Greediness):即最大比對,是比對模式的預設比對方式。也就是說在數量限定詞後不加比對模式限定符時采用的是貪婪模式。例如:

Pattern p = Pattern.compile("a.*d");
Matcher m = p.matcher("a1bcda2bcda3bc");
boolean b = m.find();
System.out.println(b);
System.out.println(m.group());
           

此時會嘗試比對最多的字元,如果更多的字元不比對時會傳回上次比對的字元,是以比對的字元串是a1bcda2bcd而不是a1bcd。

        2、懶惰模式(Laziness):也叫勉強模式(Reluctant),即最小比對。需要在數量限定詞後加?即成為該模式。例如:

Pattern p = Pattern.compile("a.*?d");
Matcher m = p.matcher("a1bcda2bcda3bc");
boolean b = m.find();
System.out.println(b);
System.out.println(m.group());
           

此時會比對最少的字元,隻要找到即可,是以比對的字元串是a1bcd而不a1bcda2bcd。

        3、占有模式(Possessive):即完全比對。需要在數量限定詞後加+即成為該模式。例如:

Pattern p = Pattern.compile("a.*+d");
Matcher m = p.matcher("a1bcda2bcda3bcd");
boolean b = m.find();
System.out.println(b);
System.out.println(m.group());
           

此時無法比對,因為正則中a之後的.*已将字元串中a之後包括最後一位最後一位d在内的剩餘的所有字元串都比對了,而正則最後還有一個d,此時字元串中已無字元,也無法回退比對相對少的子字元串,是以不能比對。由此可以看出在占有模式中,正則中的?+或*+之後不能再指定某一明确固定的字元,否則将永遠不比對。

五、非捕獲組(零寬斷言):之是以是零寬,是因為它們像^和$一樣比對的隻是位置,而不是字元。

        1、正向先行斷言:(?=pattern)表示該位置之後的字元能夠比對pattern,比如字元串a regular expression,想要比對regular中的re而不是expression中的re,可以使用re(?=gular),表明限定re右邊必須是gular。如果使用正則:re(?=gular).則傳回reg。因為斷言不會比對字元,它僅僅是一個位置。

        2、負向先行斷言:(?!pattern)表示該位置之後的字元不能比對pattern,比如字元串regex represents regular expression,想要比對除regex和regular之外的re,可以使用re(?!g),表明限定re右邊不能是g。負向和正向的差別,就在于該位置之後的字元能否比對括号中的表達式。

        3、正向後行斷言:(?<=pattern)表示該位置之前的字元能夠比對pattern,比如字元串regex represents regular expression,想要比對單詞内部的re,但不比對單詞開頭的re,可以使用(?<=\w)re。後行和先行的差別在于,後行是從右往左掃描而先行是從左往右掃描。

        4、負向後行斷言:(?<!pattern)表示該位置之前的字元不能比對pattern,比如字元串regex represents regular expression,想要比對單詞開頭的re,可以使用(?<!\w)re。