Java - 正则表达式的运用(Pattern模式和Matcher匹配)
一、緒論:?? ? ??
在寫程序的過程中,有時會需要匹配、查找、替換或者是判斷字符串的出現(xiàn)情況,而且有時不能用簡單的純編碼方式解決這些問題,這個時候就會想到要正則表達(dá)式,無論是Java, PHH, C#, ?Python, ?JavaScript, ActionScript, Perl等語言,都提高了強(qiáng)大的正則表達(dá)式支持,有的語言的精華就在于字符串處理功能比如Perl。
? ? ? ?
在Java中,正則表達(dá)式也是Java處理字符串,文本的重要工具。
Java對正則表達(dá)式的處理集中在以下兩個類: java.util.regex.Pattern ? 模式類:用來表示一個編譯過的正則表達(dá)式。 java.util.regex.Matcher ? 匹配類:用模式匹配一個字符串所得到的結(jié)果。 二、先看一個簡單的例子: 需求:從字符串:{"_type":"FQDN","_oid":"51a867e4773da1128b1422ad"} 中取出這段數(shù)字:51a867e4773da1128b1422ad 沒有用過正則表達(dá)式的程序員可能分為兩步獲取 1. 用String類的indexOf方法獲取51a867e4773da1128b1422ad的初始位置 2. 用String類的subString方法取出51a867e4773da1128b1422ad 用這種方法的缺點(diǎn)是代碼可閱讀性很差,即我們常說的hard code 如果用正則表達(dá)式則可以用以下的方法: String reg = "[0-9A-Za-z]{24,}"; // appear at least 24 times ^[0-9A-Za-z]{24,} Pattern pattern = Pattern.compile(reg); Matcher matcher = pattern.matcher(str); if (matcher.find()) {// matcher.matchers() { String fqdnId = matcher.group(); } 三、一些常見的問題 1.??鎖定模式的應(yīng)用范圍的?^ 和 $ 。他們是分別用來匹配字符串的開始和結(jié)束。? ? ? ? ?"^wangsheng":?開頭一定要有"wangsheng"的字符串才能被匹配;
?"isJoshWang$":?結(jié)尾一定要由"isJoshWang" 的字符串來結(jié)尾;
那么,
? "^abc$": 就是要求以abc開頭和以abc結(jié)尾的字符串,實(shí)際上是只有abc匹配。
?
? "notice": 匹配包含notice的字符串。
用這兩個字符就將模式鎖定在一定范圍里面。
?
2. Java對反斜線的處理問題:
在其他語言中,\\表示要插入一個字符\;?在Java語言中,\\表示要插入正則表達(dá)式的反斜線,并且后面的字符有特殊意義。
在Java正則表達(dá)式中,如果要插入一個\字符,則需要在正則表達(dá)式中寫成\\\\,原因是APIDoc定義\\表示一個反斜線。 但是如果在正則表示式中表示回車換行等,則不需要多添加反斜線了。比如回車\r就寫作\r. 3. 常用的與正則表達(dá)式相關(guān)的API 1) Matcher.find():嘗試查找與模式匹配的字符序列的下一個子序列。此方法從字符序列的開頭開始,如果該方法的前一次調(diào)用成功了并且從那時開始匹配器沒有被重置,則從以前匹配操作沒有匹配到的第一個字符開始,即如果前一次找到與模式匹配的子序列則這次從這個子序列后開始查找。 2)?Matcher.matchers():判斷整個字符序列與模式是否匹配。 當(dāng)連續(xù)用Matcher對象檢查多個字符串時候,可以使用Matcher.reset()重置匹配器,放棄其所有顯式狀態(tài)信息并將其添加位置設(shè)置為零。或者M(jìn)atcher.reset(CharSequence input)? 重置此具有新輸入序列的匹配器來重復(fù)使用匹配器。 3) 組的概念,這個概念很重要,組是用括號劃分的正則表達(dá)式,可以通過編號來引用組。組號從0開始,有幾對小括號就表示有幾個組,并且組可以嵌套,組號為0的表示整個表達(dá)式,組號為1的表示第一個組,依此類推。 例如:A(B)C(D)E正則式中有三組,組0是ABCDE,組1是B,組2是D; A((B)C)(D)E正則式中有四組:組0是ABCDE,組1是BC,組2是B;組3是C,組4是D。 int groupCount():返回匹配其模式中組的數(shù)目,不包括第0組。 String group():返回前一次匹配操作(如find())的第0組。 String group(int group):返回前一次匹配操作期間指定的組所匹配的子序列。如果該匹配成功,但指定的組未能匹配字符序列的任何部分,則返回 null。 int start(int group):返回前一次匹配操作期間指定的組所匹配的子序列的初始索引。 int end(int group):返回前一次匹配操作期間指定的組所匹配的子序列的最后索引+1。 4)匹配的范圍的控制 最變態(tài)的就要算lookingAt()方法了,名字很讓人迷惑,需要認(rèn)真看APIDoc。 start()? 返回以前匹配的初始索引。 end()? 返回最后匹配字符之后的偏移量。 public boolean lookingAt()嘗試將從區(qū)域開頭開始的輸入序列與該模式匹配。 與 matches 方法類似,此方法始終從區(qū)域的開頭開始;與之不同的是,它不需要匹配整個區(qū)域。 如果匹配成功,則可以通過 start、end 和 group 方法獲取更多信息。 返回:當(dāng)且僅當(dāng)輸入序列的前綴匹配此匹配器的模式時才返回 true。 ?5)?Pattern標(biāo)記 Pattern類的靜態(tài)方法 static Pattern compile(String regex, int flags) 將給定的正則表達(dá)式編譯到具有給定標(biāo)志的模式中。 其中的flags參數(shù)就是Pattern標(biāo)記,這個標(biāo)記在某些時候非常重要。 Pattern.CANON_EQ 啟用規(guī)范等價。 Pattern.CASE_INSENSITIVE 啟用不區(qū)分大小寫的匹配。 Pattern.COMMENTS 模式中允許空白和注釋。 Pattern.DOTALL 啟用 dotall 模式。 Pattern.LITERAL 啟用模式的字面值分析。 Pattern.MULTILINE 啟用多行模式。 Pattern.UNICODE_CASE 啟用 Unicode 感知的大小寫折疊。 Pattern.UNIX_LINES 啟用 Unix 行模式。? ?4. 字符串的替換 String.replace(char oldChar, char newChar) 返回一個新的字符串,它是通過用 newChar 替換此字符串中出現(xiàn)的所有 oldChar 而生成的。 String.replace(CharSequence target, CharSequence replacement) 使用指定的字面值替換序列替換此字符串匹配字面值目標(biāo)序列的每個子字符串。 String.replaceAll(String regex, String replacement) 使用給定的 replacement 字符串替換此字符串匹配給定的正則表達(dá)式的每個子字符串。 String.replaceFirst(String regex, String replacement) 使用給定的 replacement 字符串替換此字符串匹配給定的正則表達(dá)式的第一個子字符串。 StringBuffer.replace(int start, int end, String str) 使用給定 String 中的字符替換此序列的子字符串中的字符。 StringBuilder.replace(int, int, java.lang.String) 使用給定 String 中的字符替換此序列的子字符串中的字符。 Matcher.replaceAll(String replacement) 替換模式與給定替換字符串相匹配的輸入序列的每個子序列。 Matcher.replaceFirst(String replacement) 替換模式與給定替換字符串匹配的輸入序列的第一個子序列。? 5、 字符串的切分 String[] split(String regex) 根據(jù)給定的正則表達(dá)式的匹配來拆分此字符串。 String[] split(String regex, int limit) 根據(jù)匹配給定的正則表達(dá)式來拆分此字符串。 當(dāng)然,還有一個StringTokenizer類,可以用來切分字符串,但是現(xiàn)在SUN已經(jīng)不推薦使用了。轉(zhuǎn)變下思路,其實(shí)用正則表達(dá)式也可以達(dá)到將字符串切分為段的目的。 6.?正則表達(dá)式最大的難點(diǎn)在于熟練書寫正則表達(dá)式: 構(gòu)造 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?匹配字符?字符類?預(yù)定義字符類?POSIX 字符類(僅 US-ASCII)?java.lang.Character 類(簡單的?java 字符類型)?Unicode 塊和類別的類?邊界匹配器?Greedy 數(shù)量詞?Reluctant 數(shù)量詞?Possessive 數(shù)量詞?Logical 運(yùn)算符?Back 引用?引用?特殊構(gòu)造(非捕獲)
| x | 字符?x |
| \\ | 反斜線字符 |
| \0n | 帶有八進(jìn)制值?0?的字符?n?(0?<=?n?<=?7) |
| \0nn | 帶有八進(jìn)制值?0?的字符?nn?(0?<=?n?<=?7) |
| \0mnn | 帶有八進(jìn)制值?0?的字符?mnn(0?<=?m?<=?3、0?<=?n?<=?7) |
| \xhh | 帶有十六進(jìn)制值?0x?的字符?hh |
| \uhhhh | 帶有十六進(jìn)制值?0x?的字符?hhhh |
| \t | 制表符 ('\u0009') |
| \n | 新行(換行)符 ('\u000A') |
| \r | 回車符 ('\u000D') |
| \f | 換頁符 ('\u000C') |
| \a | 報警 (bell) 符 ('\u0007') |
| \e | 轉(zhuǎn)義符 ('\u001B') |
| \cx | 對應(yīng)于?x?的控制符 |
| [abc] | a、b?或?c(簡單類) |
| [^abc] | 任何字符,除了?a、b?或?c(否定) |
| [a-zA-Z] | a?到?z?或?A?到?Z,兩頭的字母包括在內(nèi)(范圍) |
| [a-d[m-p]] | a?到?d?或?m?到?p:[a-dm-p](并集) |
| [a-z&&[def]] | d、e?或?f(交集) |
| [a-z&&[^bc]] | a?到?z,除了?b?和?c:[ad-z](減去) |
| [a-z&&[^m-p]] | a?到?z,而非?m?到?p:[a-lq-z](減去) |
| . | 任何字符(與行結(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] |
| \p{Lower} | 小寫字母字符:[a-z] |
| \p{Upper} | 大寫字母字符:[A-Z] |
| \p{ASCII} | 所有 ASCII:[\x00-\x7F] |
| \p{Alpha} | 字母字符:[\p{Lower}\p{Upper}] |
| \p{Digit} | 十進(jìn)制數(shù)字:[0-9] |
| \p{Alnum} | 字母數(shù)字字符:[\p{Alpha}\p{Digit}] |
| \p{Punct} | 標(biāo)點(diǎn)符號:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ |
| \p{Graph} | 可見字符:[\p{Alnum}\p{Punct}] |
| \p{Print} | 可打印字符:[\p{Graph}\x20] |
| \p{Blank} | 空格或制表符:[ \t] |
| \p{Cntrl} | 控制字符:[\x00-\x1F\x7F] |
| \p{XDigit} | 十六進(jìn)制數(shù)字:[0-9a-fA-F] |
| \p{Space} | 空白字符:[ \t\n\x0B\f\r] |
| \p{javaLowerCase} | 等效于 java.lang.Character.isLowerCase() |
| \p{javaUpperCase} | 等效于 java.lang.Character.isUpperCase() |
| \p{javaWhitespace} | 等效于 java.lang.Character.isWhitespace() |
| \p{javaMirrored} | 等效于 java.lang.Character.isMirrored() |
| \p{InGreek} | Greek?塊(簡單塊)中的字符 |
| \p{Lu} | 大寫字母(簡單類別) |
| \p{Sc} | 貨幣符號 |
| \P{InGreek} | 所有字符,Greek 塊中的除外(否定) |
| [\p{L}&&[^\p{Lu}]]? | 所有字母,大寫字母除外(減去) |
| ^ | 行的開頭 |
| $ | 行的結(jié)尾 |
| \b | 單詞邊界 |
| \B | 非單詞邊界 |
| \A | 輸入的開頭 |
| \G | 上一個匹配的結(jié)尾 |
| \Z | 輸入的結(jié)尾,僅用于最后的結(jié)束符(如果有的話) |
| \z | 輸入的結(jié)尾 |
| X? | X,一次或一次也沒有 |
| X* | X,零次或多次 |
| X+ | X,一次或多次 |
| X{n} | X,恰好?n?次 |
| X{n,} | X,至少?n?次 |
| X{n,m} | X,至少?n?次,但是不超過?m?次 |
| X?? | X,一次或一次也沒有 |
| X*? | X,零次或多次 |
| X+? | X,一次或多次 |
| X{n}? | X,恰好?n?次 |
| X{n,}? | X,至少?n?次 |
| X{n,m}? | X,至少?n?次,但是不超過?m?次 |
| X?+ | X,一次或一次也沒有 |
| X*+ | X,零次或多次 |
| X++ | X,一次或多次 |
| X{n}+ | X,恰好?n?次 |
| X{n,}+ | X,至少?n?次 |
| X{n,m}+ | X,至少?n?次,但是不超過?m?次 |
| XY | X?后跟?Y |
| X|Y | X?或?Y |
| (X) | X,作為捕獲組 |
| \n | 任何匹配的?nth捕獲組 |
| \ | Nothing,但是引用以下字符 |
| \Q | Nothing,但是引用所有字符,直到?\E |
| \E | Nothing,但是結(jié)束從?\Q?開始的引用 |
| (?:X) | X,作為非捕獲組 |
| (?idmsux-idmsux)? | Nothing,但是將匹配標(biāo)志i?d?m?s?u?x?on - off |
| (?idmsux-idmsux:X)?? | X,作為帶有給定標(biāo)志?i?d?m?s?u?x?on - off |
| (?=X) | X,通過零寬度的正 lookahead |
| (?!X) | X,通過零寬度的負(fù) lookahead |
| (?<=X) | X,通過零寬度的正 lookbehind |
| (?<!X) | X,通過零寬度的負(fù) lookbehind |
| (?>X) | X,作為獨(dú)立的非捕獲組 |
反斜線、轉(zhuǎn)義和引用
反斜線字符 ('\') 用于引用轉(zhuǎn)義構(gòu)造,如上表所定義的,同時還用于引用其他將被解釋為非轉(zhuǎn)義構(gòu)造的字符。因此,表達(dá)式?\\?與單個反斜線匹配,而?\{?與左括號匹配。
在不表示轉(zhuǎn)義構(gòu)造的任何字母字符前使用反斜線都是錯誤的;它們是為將來擴(kuò)展正則表達(dá)式語言保留的??梢栽诜亲帜缸址笆褂梅葱本€,不管該字符是否非轉(zhuǎn)義構(gòu)造的一部分。
根據(jù)?Java Language Specification?的要求,Java 源代碼的字符串中的反斜線被解釋為?Unicode 轉(zhuǎn)義或其他字符轉(zhuǎn)義。因此必須在字符串字面值中使用兩個反斜線,表示正則表達(dá)式受到保護(hù),不被 Java 字節(jié)碼編譯器解釋。例如,當(dāng)解釋為正則表達(dá)式時,字符串字面值?"\b"?與單個退格字符匹配,而?"\\b"?與單詞邊界匹配。字符串字面值?"\(hello\)"?是非法的,將導(dǎo)致編譯時錯誤;要與字符串?(hello)?匹配,必須使用字符串字面值?"\\(hello\\)"。
字符類
字符類可以出現(xiàn)在其他字符類中,并且可以包含并集運(yùn)算符(隱式)和交集運(yùn)算符 (&&)。并集運(yùn)算符表示至少包含其某個操作數(shù)類中所有字符的類。交集運(yùn)算符表示包含同時位于其兩個操作數(shù)類中所有字符的類。
字符類運(yùn)算符的優(yōu)先級如下所示,按從最高到最低的順序排列:
1???? 2???? 3???? 4???? 5????| 字面值轉(zhuǎn)義???? | \x |
| 分組 | [...] |
| 范圍 | a-z |
| 并集 | [a-e][i-u] |
| 交集 | [a-z&&[aeiou]] |
注意,元字符的不同集合實(shí)際上位于字符類的內(nèi)部,而非字符類的外部。例如,正則表達(dá)式?.?在字符類內(nèi)部就失去了其特殊意義,而表達(dá)式?-?變成了形成元字符的范圍。
行結(jié)束符
行結(jié)束符?是一個或兩個字符的序列,標(biāo)記輸入字符序列的行結(jié)尾。以下代碼被識別為行結(jié)束符:
- 新行(換行)符 ('\n')、
- 后面緊跟新行符的回車符 ("\r\n")、
- 單獨(dú)的回車符 ('\r')、
- 下一行字符 ('\u0085')、
- 行分隔符 ('\u2028') 或
- 段落分隔符 ('\u2029)。
如果激活?UNIX_LINES?模式,則新行符是唯一識別的行結(jié)束符。
如果未指定?DOTALL?標(biāo)志,則正則表達(dá)式?.?可以與任何字符(行結(jié)束符除外)匹配。
默認(rèn)情況下,正則表達(dá)式?^?和?$?忽略行結(jié)束符,僅分別與整個輸入序列的開頭和結(jié)尾匹配。如果激活?MULTILINE?模式,則?^?在輸入的開頭和行結(jié)束符之后(輸入的結(jié)尾)才發(fā)生匹配。處于?MULTILINE?模式中時,$?僅在行結(jié)束符之前或輸入序列的結(jié)尾處匹配。
組和捕獲
捕獲組可以通過從左到右計算其開括號來編號。例如,在表達(dá)式?((A)(B(C)))?中,存在四個這樣的組:
1???? 2???? 3???? 4????| ((A)(B(C))) |
| \A |
| (B(C)) |
| (C) |
組零始終代表整個表達(dá)式。
之所以這樣命名捕獲組是因?yàn)樵谄ヅ渲?#xff0c;保存了與這些組匹配的輸入序列的每個子序列。捕獲的子序列稍后可以通過 Back 引用在表達(dá)式中使用,也可以在匹配操作完成后從匹配器獲取。
與組關(guān)聯(lián)的捕獲輸入始終是與組最近匹配的子序列。如果由于量化的緣故再次計算了組,則在第二次計算失敗時將保留其以前捕獲的值(如果有的話)例如,將字符串?"aba"?與表達(dá)式?(a(b)?)+?相匹配,會將第二組設(shè)置為?"b"。在每個匹配的開頭,所有捕獲的輸入都會被丟棄。
以?(?)?開頭的組是純的非捕獲?組,它不捕獲文本,也不針對組合計進(jìn)行計數(shù)。
Unicode 支持
此類符合?Unicode Technical Standard #18:Unicode Regular Expression Guidelines?第 1 級和 RL2.1 Canonical Equivalents。
Java 源代碼中的 Unicode 轉(zhuǎn)義序列(如?\u2014)是按照 Java Language Specification 的?第 3.3 節(jié)中的描述處理的。這樣的轉(zhuǎn)義序列還可以由正則表達(dá)式解析器直接實(shí)現(xiàn),以便在從文件或鍵盤擊鍵讀取的表達(dá)式中使用 Unicode 轉(zhuǎn)義。因此,可以將不相等的字符串?"\u2014"?和?"\\u2014"?編譯為相同的模式,從而與帶有十六進(jìn)制值?0x2014?的字符匹配。
與 Perl 中一樣,Unicode 塊和類別是使用?\p?和?\P?構(gòu)造編寫的。如果輸入具有屬性?prop,則與?\p{prop}?匹配,而輸入具有該屬性時與?\P{prop}?不匹配。塊使用前綴?In?指定,與在?InMongolian?中一樣??梢允褂每蛇x前綴?Is?指定類別:\p{L}?和?\p{IsL}?都表示 Unicode 字母的類別。塊和類別在字符類的內(nèi)部和外部都可以使用。
受支持的類別是由?Character?類指定版本中的?The Unicode Standard?的類別。類別名稱是在 Standard 中定義的,即標(biāo)準(zhǔn)又豐富。Pattern?所支持的塊名稱是?UnicodeBlock.forName?所接受和定義的有效塊名稱。
行為類似 java.lang.Character boolean 是?methodname?方法(廢棄的類別除外)的類別,可以通過相同的?\p{prop}?語法來提供,其中指定的屬性具有名稱?javamethodname。
與 Perl 5 相比較
Pattern?引擎用有序替換項(xiàng)執(zhí)行傳統(tǒng)上基于 NFA 的匹配,與 Perl 5 中進(jìn)行的相同。
此類不支持 Perl 構(gòu)造:
-
條件構(gòu)造?(?{X})?和?(?(condition)X|Y)、
-
嵌入式代碼構(gòu)造?(?{code})?和?(??{code})、
-
嵌入式注釋語法?(?#comment)?和
-
預(yù)處理操作?\l?\u、\L?和?\U。
此類支持但 Perl 不支持的構(gòu)造:
-
Possessive 數(shù)量詞,它可以盡可能多地進(jìn)行匹配,即使這樣做導(dǎo)致所有匹配都成功時也如此。
-
字符類并集和交集,如上文所述。
與 Perl 的顯著不同點(diǎn)是:
-
在 Perl 中,\1?到?\9?始終被解釋為 Back 引用;如果至少存在多個子表達(dá)式,則大于?9?的反斜線轉(zhuǎn)義數(shù)按 Back 引用對待,否則在可能的情況下,它將被解釋為八進(jìn)制轉(zhuǎn)義。在此類中,八進(jìn)制轉(zhuǎn)義必須始終以零開頭。在此類中,\1?到?\9?始終被解釋為 Back 引用,較大的數(shù)被接受為 Back 引用,如果在正則表達(dá)式中至少存在多個子表達(dá)式的話;否則,解析器將刪除數(shù)字,直到該數(shù)小于等于組的現(xiàn)有數(shù)或者其為一個數(shù)字。
-
Perl 使用?g?標(biāo)志請求恢復(fù)最后匹配丟失的匹配。此功能是由?Matcher?類顯式提供的:重復(fù)執(zhí)行?find?方法調(diào)用可以恢復(fù)丟失的最后匹配,除非匹配器被重置。
-
在 Perl 中,位于表達(dá)式頂級的嵌入式標(biāo)記對整個表達(dá)式都有影響。在此類中,嵌入式標(biāo)志始終在它們出現(xiàn)的時候才起作用,不管它們位于頂級還是組中;在后一種情況下,與在 Perl 中類似,標(biāo)志在組的結(jié)尾處還原。
-
Perl 允許錯誤匹配構(gòu)造,如在表達(dá)式?*a?中,以及不匹配的括號,如在在表達(dá)式?abc]?中,并將其作為字面值對待。此類還接受不匹配的括號,但對 +、? 和 * 不匹配元字符有嚴(yán)格限制;如果遇到它們,則拋出?PatternSyntaxException。
有關(guān)正則表達(dá)式構(gòu)造行為更準(zhǔn)確的描述,請參見?Mastering Regular Expressions, 2nd Edition,該書由 Jeffrey E. F. Friedl、O'Reilly 和 Associates 合著,于 2002 年出版。
from:?http://josh-persistence.iteye.com/blog/1881270
總結(jié)
以上是生活随笔為你收集整理的Java - 正则表达式的运用(Pattern模式和Matcher匹配)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第 16 章 反射(Reflection
- 下一篇: JAVA正则表达式:Pattern类与M