Java 正則表達(dá)式全攻略(二)
labels:Java 正則表達(dá)式全攻略(二) java正則表達(dá)式 字符集
正則表達(dá)式引擎的內(nèi)部工作機制
知道正則表達(dá)式引擎是如何工作的,將有助于你很快理解為何某個正則表達(dá)式不像你期望的那樣工作,還可以 使你清楚如何對表達(dá)式進(jìn)行性能優(yōu)化。從最基本的正則表達(dá)式引擎實現(xiàn)思路上來分的話,有兩種:確定型有限狀態(tài)機(Deterministic Finite-State Automaton)簡稱DFA和不確定型有限狀態(tài)機(Nodeterministic Finite-State Automaton)簡稱NFA,也有人稱其為文本導(dǎo)向和正則導(dǎo)向。以下這個網(wǎng)址 http://osteele.com/tools/reanimator/ 以一種非常直觀的方式說明了 DFA 和 NFA 對相同的表達(dá)式的不同編譯結(jié)果。
由于我們的目的不在于學(xué)習(xí)狀態(tài)機,所以我們忽略這2者的工作原理,直接對比他們的影響。就拿表達(dá)式 a|ab|abc|abcd 來對比。 我們可以看到NFA的結(jié)果比較復(fù)雜,而DFA十分簡潔,這是否又會影響到2者的性能呢?確實如此,DFA的執(zhí)行速度與表達(dá)式無關(guān),它在編譯時的優(yōu)化已經(jīng)優(yōu) 于大多數(shù) NFA引擎的復(fù)雜優(yōu)化措施。而NFA的執(zhí)行速度與表達(dá)式有著直接的關(guān)系。從匹配結(jié)果來看,DFA總是返回最左邊最長的匹配結(jié)果,而NFA總是比較猴急,總 會匹配第一個找到的結(jié)果。根據(jù)這一點,我們可以輕易分辨出所使用的引擎是DFA還是NFA,你可以使用表達(dá)式 nfa|nfa not 對字符串”nfa not”進(jìn)行測試,如果匹配結(jié)果是 nfa ,那該引擎是NFA的,而Java就是屬于NFA的。最后一點就是,NFA能提供的功能比DFA更多,例如:捕獲由括號內(nèi)的子表達(dá)式匹配的文本、環(huán)視,以 及其他復(fù)雜的零長度確認(rèn)、“惰性”量詞等。而我們講的是Java的正則表達(dá)式,那當(dāng)然也就是在說NFA啦,而NFA由于功能比較多用起來比較方便,因此比 DFA要流行些。
正則導(dǎo)向的引擎總是返回最左邊的匹配
這是需要你理解的很重要的一點:即使以后有可能發(fā)現(xiàn)一個“更好”的匹配,正則導(dǎo)向的引擎也總是返回最左邊的匹配。 當(dāng)把 cat 應(yīng)用到“He captured a catfish for his cat”,引擎先比較 c 和“H”,結(jié)果失敗了。于是引擎再比較 c 和“e”,也失敗了。直到第四個字符, c 匹配了“c”。 a 匹配了第五個字符。到第六個字符 t 沒能匹配“p”,也失敗了。引擎再繼續(xù)從第五個字符重新檢查匹配性。直到第十五個字符開始, cat 匹配上了“catfish”中的“cat”,正則表達(dá)式引擎急切的返回第一個匹配的結(jié)果,而不會再繼續(xù)查找是否有其他更好的匹配。
字符集
字符集是由一對方括號“[]”括起來的字符集合。使用字符集,你可以告訴正則表達(dá)式引擎僅僅匹配多個字符中的一個。如果你想匹配一個“a”或一個“e”,使用 [ae] 。你可以使用 gr[ae]y 匹配gray或grey。這在你不確定你要搜索的字符是采用美國英語還是英國英語時特別有用。相反, gr[ae]y 將不會匹配graay或graey。字符集中的字符順序并沒有什么關(guān)系,結(jié)果都是相同的。
你可以使用連字符“-”定義一個字符范圍作為字符集。 [0-9] 匹配0到9之間的單個數(shù)字。你可以使用不止一個范圍。 [0-9a-fA-F] 匹配單個的十六進(jìn)制數(shù)字,并且大小寫不敏感。你也可以結(jié)合范圍定義與單個字符定義。 [0-9a-fxA-FX] 匹配一個十六進(jìn)制數(shù)字或字母X。再次強調(diào)一下,字符和范圍定義的先后順序?qū)Y(jié)果沒有影響。
取反字符集
在左方括號“[”后面緊跟一個尖括號“^”,將會對字符集取反。結(jié)果是字符集將匹配任何不在方括號中的字符。不像“.”,取反字符集是可以匹配回車換行符的。
需要記住的很重要的一點是,取反字符集必須要匹配一個字符。 q[^u] 并不意味著:匹配一個q,后面沒有u跟著。它意味著:匹配一個q,后面跟著一個不是u的字符。所以它不會匹配“Iraq”中的q,而會匹配“Iraq is a country”中的q和一個空格符。事實上,空格符是匹配中的一部分,因為它是一個“不是u的字符”。如果你只想匹配一個q,條件是q后面有一個不是u 的字符,我們可以用后面將講到的向前查看來解決。
字符集中的元字符
需要注意的是,在字符集中只有4個 字符具有特殊含義。它們是:“ ] \ ^ - ”。“]”代表字符集定義的結(jié)束;“\”代表轉(zhuǎn)義;“^”代表取反;“-”代表范圍定義。其他常見的元字符在字符集定義內(nèi)部都是正常字符,不需要轉(zhuǎn)義。例如,要搜索星號*或加號+,你可以用 [+*] 。當(dāng)然,如果你對那些通常的元字符進(jìn)行轉(zhuǎn)義,你的正則表達(dá)式一樣會工作得很好,但是這會降低可讀性。
在字符集定義中為了將反斜杠“\”作為一個文字字符而非特殊含義的字符,你需要用另一個反斜杠對它進(jìn)行轉(zhuǎn)義。 [\\x] 將會匹配一個反斜杠和一個X。“]^-”都可以用反斜杠進(jìn)行轉(zhuǎn)義,或者將他們放在一個不可能使用到他們特殊含義的位置。我們推薦后者,因為這樣可以增加可讀性。比如對于字符“^”,將它放在除了左括號“[”后面的位置,使用的都是文字字符含義而非取反含義。如 [x^] 會匹配一個x或^。 []x] 會匹配一個“]”或“x”。 [-x] 或 [x-] 都會匹配一個“-”或“x”。
字符集的簡寫
因為一些字符集非常常用,所以有一些簡寫方式。
. | 任何字符(與行結(jié)束符可能匹配也可能不匹配) |
\d | 數(shù)字: [0-9] |
\D | 非數(shù)字: [^0-9] |
\s | 空白字符: [\t\n\x0b\f\r] |
\S | 非空白字符: [^\s] |
\w | 單詞字符: [a-zA-Z_0-9] |
\W | 非單詞字符: [^\w] |
字符集的重復(fù)
如果你用“ ?*+ ”操作符來重復(fù)一個字符集,你將會重復(fù)整個字符集。而不僅是它匹配的那個字符。正則表達(dá)式 [0-9]+ 會匹配837以及222。如果你僅僅想重復(fù)被匹配的那個字符,可以用向后引用達(dá)到目的。我們以后將講到向后引用。
* | 重復(fù)零次或更多次 |
+ | 重復(fù)一次或更多次 |
? | 重復(fù)零次或一次 |
{n} | 重復(fù)n次 |
{n,} | 重復(fù)n次到更多次 |
{n,m} | 重復(fù)n到m次 |
結(jié)合前面的知識,我們就可以寫出以下這類常用的表達(dá)式:
1: // 判斷字符串是否一個合法的16進(jìn)制
2: String regex = "[-+]?0[xX]?[0-9a-fA-F]+" ;
3: System.out.println( "0xFF" .matches(regex)); // true
4: System.out.println( "-0Xff" .matches(regex)); // true
5: System.out.println( "ff" .matches(regex)); // false
6: System.out.println( "0x1H" .matches(regex)); // false
7: // 簡單地判斷一個字符串是否合法的身份證號碼
8: regex = "\\d{15}|\\d{18}" ;
9: System.out.println( "440104700101001" .matches(regex)); // ture;
10: System.out.println( "44010700101001" .matches(regex)); // false;
11: System.out.println( "440104197001010015" .matches(regex)); // ture;
12: System.out.println( "4401041970010100015" .matches(regex)); // false;
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
