Java Regular Expression的學習筆記 - JWorld@TW Java論壇

文章推薦指數: 80 %
投票人數:10人

Java 技巧文件- Java Regular Expression的學習筆記. ... x : 一個字元,例如"a" 表示要找含有這個字元的部分。

[abc] : 這個字元可能是a或b或c JWorld@TW thebestprofessionalJavasiteinTaiwan       註冊| 登入| 全文檢索| 排行榜   » JWorld@TW » Java技巧文件    列印話題    寄給朋友    訂閱主題 bybrowserat2008-03-1011:29 reqular-expressions kebin_liu 雲端決策系統 版主 發文:1861 積分:11 於2005-10-1223:42 雖然ReqularExpressions(以下簡稱REs)在這個論壇或是其他網站都可以找到相當多的資料,但是當我自己要學的時候才發現有很多小地方還是看不懂,所以才以javaAPI裡面的說明為主,把每個符號的解釋一一弄懂,終於對REs有了初步的認識。

所以這份文件是以javaAPI提到的符號解釋,加上我自己的心得及範例所整理出來的,我用"字元"和"字元組成",這兩大部分來解釋REs的符號,大部分的解釋都有範例,這樣比較容易瞭解,沒有範例的部分不是太簡單,就是我找不到例子,不過對於認識REs應該沒有多大影響。

因為我算是REs的初學者,所以難免有觀念上的錯誤,加上很多"冷門"的符號,要找到正確的解釋都很困難,因此我沒把握所有的資料都是正確的,如果各位有發現錯誤,還請加以指正。

首先是字元,REs的基本元素就是字元,所以對於字元有相當細膩的描述方式,而且很多時候描述方式都不是唯一的,所以不必拘泥於找到最完美的寫法。

字元分兩部分來解釋。

1.一般字元,在還沒做字元組合時,下面這些都只是"一個"字元,先要有這個觀念,才不容易弄不清楚字串和字元的區別。

x:一個字元,例如"a"表示要找含有這個字元的部分。

[abc]:這個字元可能是a或b或c[^abc]:這個字元是除了"a""b""c"以外的。

[a-zA-Z]:這個字元是a到z或是A到Z。

[a-d[m-p]]:這個字元是a到d或是m到p(聯集)。

[a-z&&[def]]:這個字元是"d","e",or"f"(交集)。

[a-z&&[^bc]]:這個字元是a-z但bc除外,等價於[ad-z](差集)。

[a-z&&[^m-p]]:這個字元是a-z但m-p除外,等價於[a-lq-z](差集)。

除了放在一開始的"^"以及兩個"&&"以外,放在[]裡面的都當一般字元。

可是\比較特殊,要\\才能當一個\,所以程式裡面必須寫\\\\,例如,想查"\"用[]包起來要寫成"[\\]",可是java在字串裡面要寫Stringpattern="[\\\\]"。

2.特殊字元.:所有的字元,不一定包含換行。

javaREs的"."預設是不包含"\r"和"\n"的,但是可以用(?s)來讓"."等於所有字元,(?s)的用法下面還有說明。

\r:Carriagereturn。

\t:TAB。

\n:換行。

\f:換頁。

\e:escape。

\d:數字0-9。

\D:非數字,數字的除外集合。

\s:會產生空白的字元也就是[\t\n\x0B\f\r],也就是""(空白)、"\t"、"\n"、"\x0B"、"\f"、"\r"。

\S:非會產生空白的字元,上面的除外集合。

\w:文字a-zA-Z_0-9,所有的英文大小寫,數字和底線。

\W:非文字,文字的除外集合。

\:把之後的特殊字元當作是一般是字元,例如"\\"等於一個"\","\["等於"["。

\Q\E:\Q到\E符號之間的特殊符號都當一般字元處理,例如"\Q(?:X)\E",符合的字串是"(?:X)"^:行首,例如"^e"會找出所有在行首的"e",在[]裡面如果是第一個代表反相,如果不是第一個也當一般字元。

$:行尾,例如"e$"會找出所有在結尾的"e"。

\b:符合文字邊界(wordboundary)。

也就是說在字與空格之間的位置例如,'re\b'符合"are"裡的're'但是不符合"area"裡的're'\B:符合非文字邊界例如,'re\B'符合"area"裡的're'但是不符合"are"裡的're'\A:輸入的開始。

\G:前一個符合的結尾的地方。

\Z:輸入的結尾去掉結尾符號的部分。

例如字串"ABC\n",pattern"ABC\Z"就可以取得"ABC"。

\z:輸入的結尾。

例如字串"ABC\n",pattern"ABC\z"就不相符。

所謂的輸入,是指一次的處理資料,例如Strings="ABC\nABC\tABC",就算一個輸入(inputsequence)。

到這裡為止是對字元的描述,字元的所有的可能情況應該都可以涵蓋了,可是光是字元是沒辦法構成字串,所以接下來是把字元組成字串的方法。

字元組成,字元組成分三部分來解釋,下面符號的X和Y可以是一個字元也可以是一個Group。

1.簡單組合,就是把字元排在一起。

XY:單純排列在一起,例如"ab"就是找"ab"這個字串,和"[ab]"不一樣,"[ab]"是代表"一個字元"可能是a或b。

X|Y:XorY,例如a|b,而以字元而言a|b就等於[ab],所以對單一字元效果不大,主要是用於字元範圍的[]|[]或者群組()|()比較有意義。

例如[a-z]|[0-9]表示不是小寫就是數字,(abc)|(123)表示是"abc"或是"123"。

實例"c|Car"相符字串是"c"或"Car"。

另外abc|def是指"abc"或"def",而不是"ab[cd]ef"。

(X):群組,將多個字元包裝成一個群組,和單純排列不同的地方是,群組可以參照,也可以對群組設定出現次數,例如(abc)+是指"abc"出現一次以上,abc+是指ab和一次以上的c。

群組參照舉例來說比較容易懂,例如"(.)(.)(.).?\3\2\1",可以找出3個字的回文。

如"abccba"、"xcdfdcx"。

群組還有一個值得注意的是,群組0是留給整體的比對的結果,例如上面的例子group0是"abccba",group1是"a"、group2是"b"、group3是"c"。

有了群組參照的觀念,後面的non-capturinggroup就會比較容易瞭解。



Group的另一個對應符號是\m:m是數字,表示參照前面的group,如上述的範例。

2.重複次數出現次數接在字元之後,表示這個字元出現的次數,接在Group之後就表示group的出現次數。

次數描述有三種quantifiers,GreedyquantifiersX?:X出現0或一次X+:X出現一次以上X*:X出現0或一次以上X{n,}:X出現至少n次X{n,m}:X出現n到m次X{n}:X出現n次ReluctantquantifiersX??:X出現0或一次X+?:X出現一次以上X*?:X出現0或一次以上X{n,}?:X出現至少n次X{n,m}?:X出現n到m次X{n}?:X出現n次PossessivequantifiersX?+:X出現0或一次X++:X出現一次以上X*+:X出現0或一次以上X{n,}+:X出現至少n次X{n,m}+:X出現n到m次X{n}+:X出現n次光看這樣的說明是無法分出三者不同,以下舉例說明。

Greedyquantifiers字串"xfooxxxxxxfoo"pattern".*foo"結果xfooxxxxxxfooGreedy字面翻譯是貪婪,也就是盡可能的取字串,其實最貪婪的是第三種方法,因為Greedy還會把之後相符的資料留下來,Possessive吃的連骨頭都不剩。

Reluctantquantifiers字串"xfooxxxxxxfoo"pattern".*?foo"結果xfoo和xxxxxxfooReluctant字面翻譯是勉強,也就是抓最小可能,像這個例子,第一次抓一個x之後發現後面和foo相符,就得第一個結果,然後一直到最後又得到第二個結果。

Possessivequantifiers字串"xfooxxxxxxfoo"pattern".*+foo"結果沒有相符合資料,因為所有的資料都與"."比較相符,最後沒有剩下的字串可以和foo做比較,所以沒有符合資料。

3.Specialconstructs(non-capturing)所謂的non-capturing就是說這個group會被比對,但是不會暫存在group裡面,就是最後得到的Group裡面不會有這組資料。

(?:X):X會取得,但不會被保留,當之後有用\m的時候,這個Group會不算在內,這樣的處理效能會比較好。

(?idmsux):特別設定的flag設為on。

(?-i-d-m-s-u-x):特別設定的flag設為off。

idmsux的說明如下:iCASE_INSENSITIVE:就是不分大小寫。

(?i)例如字串"ABC"pattern用"abc"會找不到,用"(?i)abc"就會找到"ABC"。

dUNIX_LINES:\n當作換行,當文件是UNIX的換行格式時,要處理換行就可以打開這個模式。

(?d)mMULTILINE:多行模式下,^和$是以指每一行,不然是用整個字串的頭尾當^和$。

(?m)例如字串"ABC\nABC\nABC";pattern"^ABC$"會找不到,"(?m)^ABC$"才會找到三個"ABC";sDOTALL:預設java的.不含\n\r,這個模式可以讓.等於所有字元包含\r\n。

(?s)例如字串"htm\nhtm\nhtm"pattern用".htm"會找不到,用"(?s).htm"就會找到後面兩個"\nhtm"uUNICODE_CASE:unicode模式。

(?u)xCOMMENTS:可以在pattern裡面使用註解,會忽略pattern裡面的whitespace,以及"#"一直到結尾。

(?x)例如字串"ABC"pattern用"ABC#找字串ABC"會找不到,用"(?x)ABC#找字串ABC",就會找到"ABC"。

(?idmsux-idmsux:X):X是non-capturinggroup並且設定flagson-off。

X(?=X):lookahead在要取得的字串右邊,接著X但X不被算在內。

例如Jack(?=Sprat)則只有JackSprat的Jack會被取得,Jack(?=Sprat|Frost),則只有JackSprat和JackFrost的Jack都符合。

X(?!X):lookahead在要取得的字串右邊,和上面相反,例如Jack(?!Sprat)則後面是Sprat的Jack不會被取得。

(?<=X)X:lookbehind在要取得的字串左邊,例如"(?<=foo)bar",找接在foo之後的"bar"。

還有裡面的文字必須已知長度,也就是不能用"(?<=foo+)""(?<=foo*)""(?<=foo{1,})",但是可以用"(?<=foo?)""(?<=foo{1,2})""(?<=foo{1})"。

(?X):X,asanindependent,non-capturinggroup。

因為這幾個符號都是non-capturing的,所以會有一個現象,直接看下面範例會比較容易瞭解字串"abc"pattern"a(?:b)c"結果"abc"但是b沒有變成group1,這也就是non-capturing。

如果用"a(b)c"會得到group0是"abc",group1是b。

字串"abc"pattern"a(?=b)c"結果抓不到,因為b並沒有被取出,要"a(?=b).c"才抓的到,而且"a(?=b).c"只會與"abc"相符,不會與"acc"等等相符。

字串"abc"pattern"a(?<=b)c"結果抓不到,因為b並沒有被取出,要"a.(?<=b)c"才抓的到,而且"a(?<=b).c"只會與"abc"相符,不會與"acc"等等相符。

由上面例子可知,這幾個符號都不會把符合的字串取出,也就是會比對但是不會算到結果裡面(non-capturing)。

所以lookahead和lookbehind那四個符號,不適合放在字串中間,另外,因為這幾個符號都是non-capturing,所以在後面加上大於0的次數都和一次是一樣的,例如字串"XB",pattern"(?<=X){9}B"一樣可以取得B,而pattern""(?<=A){0}B"也可以取得"B"。

整個java的REs大概就這些了,只是看完這些解釋其實離可以運用還有一小段距離,因為REs需要的是分析pattern的能力,而這種能力要多練習才會。

在自己能寫出pattern之前,可以拿別人寫好的pattern來測試體會一下,下面是一個簡單的測試程式。

這個程式是我小修改網路上找到的範例,會回傳符合的group,方便測試結果。

123456789101112131415161718192021222324importjava.util.regex.Matcher; importjava.util.regex.Pattern;   publicclassTestRegular{ publicstaticvoidmain(String[]args){ StringinputStr="ABC\nABC\nABC"; StringpatternStr="(?d)ABC"; Patternpattern=Pattern.compile(patternStr); Matchermatcher=pattern.matcher(inputStr); booleanmatchFound=matcher.find(); while(matchFound){ System.out.println(matcher.start()+"-"+matcher.end()); for(inti=0;i<=matcher.groupCount();i++){ StringgroupStr=matcher.group(i); System.out.println(i+":"+groupStr); } if(matcher.end()+1<=inputStr.length()){ matchFound=matcher.find(matcher.end());   }else{     break;   }   }   } } 測試REs也可以使用一外部工具,例如eclipse的pluginRegextester,我很多範例跟觀念都是用這個工具去測試的。

最後"反組譯"幾個例子來練習,就是把別人寫好的pattern試著解釋出來。

HTMLTAG?[a-z][a-z0-9]*[^<>]*>開始是"的字元不限次數個,最後以">"結尾。

相符的是""""""等。

不相符字串"<123>"HTMLTAG之二]*>(.*?)\1>開始是"字元,然後是一個">",不限定個數的字元(用*?才不會一直取到之後的tag去),然後是tag結束的"",要和第一個Group的值match,最後以">"結束。

相符的有"Test""

"
Test
"""不相符字串"""<123>123>"IP\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b前後都是"\b"表示在獨立的一個字的單元,用"?:"是不算group而已,250-255或200-249或0-199,接著一個".",這樣的group有3次,最後再一次0-255的group。

這樣就是一個0.0.0.0-255.255.255.255的IP的pattern,而[0-9][0-9]?也可以改成[0-9]{1,2}。

相符的字串"140.115.83.240""255.255.0.0"不相符字串"256.1.1.2""-1.300.1.2"IP之二\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\bIP之三[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}這是0.0.0.0-999.999.999.999的格式其他例子"[hc]+at"相符的有"hat","cat","hhat","chat","hcat","ccchat"etc."[hc]?at"相符的有"hat","cat"and"at""([cC]at)|([dD]og)"相符的有"cat","Cat","dog"and"Dog""/\*.*\*/"相符的有/*Secondcomment*/以下幾個(都是抄的)適合檢查輸入的值,因為都從^到$,從字串開始到結束。

1、非負整數:”^\d+$”2、正整數:”^[0-9]*[1-9][0-9]*$”3、非正整數:”^((-\d+)|(0+))$”4、負整數:”^-[0-9]*[1-9][0-9]*$”5、整數:”^-?\d+$”6、非負浮點數:”^\d+(\.\d+)?$”7、正浮點數:”^((0-9)+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$”8、非正浮點數:”^((-\d+\.\d+)?)|(0+(\.0+)?))$”9、負浮點數:”^(-((正浮點數正則式)))$”10、英文字符串:”^[A-Za-z]+$”11、英文大寫串:”^[A-Z]+$”12、英文小寫串:”^[a-z]+$”13、英文字符數字串:”^[A-Za-z0-9]+$”14、英數字加下劃線串:”^\w+$”15、E-mail地址:”^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$”16、URL:”^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$”-------學習REs最重要是學會找到字串出現的特徵,特徵有時候是出現的位置,例如位置是行首(^),是行尾($),單字開始(\b),單字裡面(\B),有時候是前後出現的字串,例如要找htmltag的屬性,都會由""結束。

而要抓""的url就可以從"href"開始。

但有時候要找的字並沒有特殊的位置,也沒關係,就把要找的字描述出來也就可以了,例如要抓日期就做一個"(0[1-9]|[12][0-9]|3[01])[-/.](0[1-9]|1[012])[-/.](19|20)[0-9]{2}"的pattern就可以了。

而其實真正困難的地方在於很多pattern都要看到結果才會瞭解哪裡錯了。

看這個例子字串"ABCDC"pattern"([A-Z]*).\1"預期要抓到CDC的,結果卻是"A""B""CDC",因為*是0~n所以沒抓也算,而後面的\1如果前面group沒抓到,他也跟著什麼都沒有,最後只有"."抓到第一個結果的"A",同理抓到第二個結果的"B",然後才抓到預期的"CDC",而改成"([A-Z]+).\1"就只會抓到"CDC"了。

最後誰能幫忙解釋一下"(?>X)",因為我實在分不出來他和(?:X)有什麼差別.... kebin_liueditedon2008-08-0115:14 T55555 Java,Ruby,Haskell 版主 發文:1026 積分:24 於2005-10-1302:06 kebin_liuwrote:...實例"c|Car"(等同於"[cC]ar")相符字串是"car"或"Car"。

...最後誰能幫忙解釋一下"(?>X)",因為我實在分不出來他和(?:X)有什麼差別....(1)TypoError.Itshouldbe:實例"(?:c|C)ar"(等同於"[cC]ar")相符字串是"car"或"Car"。

("c|Car"willmatch"c"or"Car")Andalthough(c|C)isthesameas[cC],theperformancemaydifferent,check:http://www.javaworld.com.tw/jute/post/view?bid=5&id=43615&sty=3&age=0&tpg=1&ppg=1#43615(2)JavaAPIsaid(?>X)X,asanindependent,non-capturinggroupInfact,the(?>X)is*Atomicgrouping*,Textmatchedwithinthegroupisneverbacktrackedinto,evenifthisleadstoamatchfailure.Hereismyexamplethatletsyouknowthedifferenctbetween(?>X)and(?:X)123456789importjava.util.regex.*; publicclassTest{ publicstaticvoidmain(String[]args)throwsException{ System.out.println(Pattern.matches("(?>[ab]*)\\w\\w","aabbcc"));#true System.out.println(Pattern.matches("(?:[ab]*)\\w\\w","aabbcc"));#true System.out.println(Pattern.matches("(?>[ab]*)\\w\\w","aabbaa"));#false! System.out.println(Pattern.matches("(?:[ab]*)\\w\\w","aabbaa"));#true } } T55555editedon2005-10-1303:14 kebin_liu 雲端決策系統 版主 發文:1861 積分:11 於2005-10-1316:23 T55555wrote:.....(2)JavaAPIsaid(?>X)X,asanindependent,non-capturinggroupInfact,the(?>X)is*Atomicgrouping*,Textmatchedwithinthegroupisneverbacktrackedinto,evenifthisleadstoamatchfailure.....嗯,這樣就懂了。

謝謝囉... emt2a7 發文:71 積分:3 於2006-05-1711:32 我來Demo一下我正在做的某片斷的程式碼:123456789101112131415161718Stringlocal="zh";//地區語言 Stringcountry="TW";//國家代碼   Patternpattern=Pattern.compile("^[A-Za-z]+$");//只要英文字母大小寫,其餘不要 Matchermatcher=pattern.matcher(local);   if(matcher.find()){ System.out.println("地區matcherOK"); }else{ System.out.println("地區matcherNOOK"); }   matcher=pattern.matcher(country); if(matcher.matches()){ System.out.println("國家matcherOK"); }else{ System.out.println("國家matcherNOOK"); } 結論:1.當User輸入地區,國家後2.用程式碼來對地區,國家是否有非英文字母的檢查PS.小小Demo,見笑... tommy567332 發文:9 積分:0 於2008-07-2214:42 看了這麼多~頭真的有點暈~還想請教下面這個問題,不知道有沒有更好的學習方法?我連下面這行我都不懂呢?例如:str.matches("
")想請教大大下面這四個是代表什麼?謝謝!(1)a.+(2)href*=*(3)['\"](4)?.* kebin_liu 雲端決策系統 版主 發文:1861 積分:11 於2008-07-2217:37 tommy567332wrote:看了這麼多~頭真的有點暈~還想請教下面這個問題,不知道有沒有更好的學習方法?我連下面這行我都不懂呢?例如:str.matches("")想請教大大下面這四個是代表什麼?謝謝!(1)a.+(2)href*=*(3)['\"](4)?.*(1)a開頭之後任意字元一個以上,例如.abcaaaair(2)hre之後f出現0~n次,然後=出現0~n次例如.hre=hreff==hrefffffff(3)一個字元,是'或"(4)?是描述前面的['\"],表示出現0或一次,接著是.表示任一字元,*是0~n次。

例如."abcd'abcefaaa這個pattern看來是要判斷html的tag,但是有很大的問題,正確的html的tag會回傳true,但是很多錯誤情況也會回傳true例如等等 用網路當作交際工具是一種病態,但當大家都病態之後,他就是一種常態。

tommy567332 發文:9 積分:0 於2008-07-2308:54 kebin_liuwrote:(1)a開頭之後任意字元一個以上,例如.abcaaaair(2)hre之後f出現0~n次,然後=出現0~n次例如.hre=hreff==hrefffffff(3)一個字元,是'或"(4)?是描述前面的['\"],表示出現0或一次,接著是.表示任一字元,*是0~n次。

例如."abcd'abcefaaa這個pattern看來是要判斷html的
tag,但是有很大的問題,正確的html的tag會回傳true,但是很多錯誤情況也會回傳true例如等等感謝呢!說的很清楚~ KiahStarck 發文:7 積分:0 於2015-06-2817:00 Kebin朋友你太強大了!感謝你的資料!不過小弟有點不懂為啥在[]中就要打四個\\\\呢? » JWorld@TW »  Java技巧文件  已讀文章 新的文章 被刪除的文章 JWorld@TW 本站商標資訊 PoweredbyPowerfulJuteForum®VersionJute1.5.8



請為這篇文章評分?