Java正則表達式之Pattern類實例詳解
前言
這個系列的文章我們使用以下的順序進行講解:
- Pattern 詳解;
- Matcher 詳解;
- 正則表達式語法詳解。
接下來先來介紹 Pattern 類。
在Java中,java.util.regex包定義瞭正則表達式使用到的相關類,其中最主要的兩個類為:Pattern、Matcher:
- Pattern 編譯正則表達式後創建一個匹配模式;
- Matcher 使用Pattern實例提供的正則表達式對目標字符串進行匹配,是真正影響搜索的對象。。
另加一個新的例外類,PatternSyntaxException,當遇到不合法的搜索模式時,會拋出例外。
Pattern 概述
聲明:public final class Pattern implements java.io.Serializable
Pattern 類有final修飾,可知他不能被子類繼承。
含義:模式類,正則表達式的編譯表示形式。
註意:此類的實例是不可變的,可供多個並發線程安全使用。
Pattern 匹配模式(Pattern flags)
compile( )方法有一個版本,它需要一個控制正則表達式的匹配行為的參數:
Pattern Pattern.compile(String regex, int flag)
flag 的取值范圍
字段 | 說明 |
---|---|
Pattern.UNIX_LINES | unix行模式,大多數系統的行都是以\n結尾的,但是少數系統,比如Windows,卻是以\r\n組合來結尾的,啟用這個模式之後,將會隻以\n作為行結束符,這會影響到^、$和點號(點號匹配換行符)。 通過嵌入式標志表達式 (?d) 也可以啟用 Unix 行模式。 |
Pattern.CASE_INSENSITIVE | 默認情況下,大小寫不敏感的匹配隻適用於US-ASCII字符集。這個標志能讓表達式忽略大小寫進行匹配。要想對Unicode字符進行大小不明感的匹配,隻要將UNICODE_CASE與這個標志合起來就行瞭。 通過嵌入式標志表達式(?i)也可以啟用不區分大小寫的匹配。 指定此標志可能對性能產生一些影響。 |
Pattern.COMMENTS ⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢⇢ | 這種模式下,匹配時會忽略(正則表達式裡的)空格字符(不是指表達式裡的”//s”,而是指表達式裡的空格,tab,回車之類)和註釋(從#開始,一直到這行結束)。 通過嵌入式標志表達式(?x) 也可以啟用註釋模式。 |
Pattern.MULTILINE | 默認情況下,輸入的字符串被看作是一行,即便是這一行中包好瞭換行符也被看作一行。當匹配“^”到“$”之間的內容的時候,整個輸入被看成一個一行。啟用多行模式之後,包含換行符的輸入將被自動轉換成多行,然後進行匹配。 通過嵌入式標志表達式 (?m) 也可以啟用多行模式。 |
Pattern.LITERAL | 啟用字面值解析模式。 指定此標志後,指定模式的輸入字符串就會作為字面值字符序列來對待。輸入序列中的元字符或轉義序列不具有任何特殊意義。 標志 CASE_INSENSITIVE 和 UNICODE_CASE 在與此標志一起使用時將對匹配產生影響。其他標志都變得多餘瞭。 不存在可以啟用字面值解析的嵌入式標志字符。 |
Pattern.DOTALL | 在這種模式中,表達式 .可以匹配任何字符,包括行結束符。默認情況下,此表達式不匹配行結束符。 通過嵌入式標志表達式 (?s) 也可以啟用此種模式(s 是 “single-line” 模式的助記符,在 Perl 中也使用它)。 |
Pattern.UNICODE_CASE | 在這個模式下,如果你還啟用瞭CASE_INSENSITIVE標志,那麼它會對Unicode字符進行大小寫不敏感的匹配。默認情況下,大小寫不明感的匹配隻適用於US-ASCII字符集。 指定此標志可能對性能產生影響。 |
Pattern.CANON_EQ | 當且僅當兩個字符的正規分解(canonical decomposition)都完全相同的情況下,才認定匹配。比如用瞭這個標志之後,表達式a/u030A會匹配?。默認情況下,不考慮規范相等性(canonical equivalence)。 指定此標志可能對性能產生影響。 |
在這些標志裡面,Pattern.CASE_INSENSITIVE,Pattern.MULTILINE,以及Pattern.COMMENTS是最有用的(其中Pattern.COMMENTS還能幫我們把思路理清楚,並且/或者做文檔)。註意,你可以用在表達式裡插記號的方式來啟用絕大多數的模式。這些記號就在上面那張表的各個標志的下面。你希望模式從哪裡開始啟動,就在哪裡插記號。
可以用OR (|)運算符把這些標志配合使用。
代碼示例
多行模式:Pattern.MULTILINE 示例
我測試瞭一下,也就是說如果沒有 MULTILINE 標志的話, ^ 和 $ 隻能匹配輸入序列的開始和結束;否則,就可以匹配輸入序列內部的行結束符。測試代碼如下:
import java.util.regex.*; /** * 多行模式 */ public class ReFlags_MULTILINE { public static void main(String[] args) { // 註意裡面的換行符 String str = "hello world\r\n" + "hello java\r\n" + "hello java"; System.out.println("===========匹配字符串開頭(非多行模式)==========="); Pattern p = Pattern.compile("^hello"); Matcher m = p.matcher(str); while (m.find()) { System.out.println(m.group() + " 位置:[" + m.start() + "," + m.end() + "]"); } System.out.println("===========匹配字符串開頭(多行模式)==========="); p = Pattern.compile("^hello", Pattern.MULTILINE); m = p.matcher(str); while (m.find()) { System.out.println(m.group() + " 位置:[" + m.start() + "," + m.end() + "]"); } System.out.println("===========匹配字符串結尾(非多行模式)==========="); p = Pattern.compile("java$"); m = p.matcher(str); while (m.find()) { System.out.println(m.group() + " 位置:[" + m.start() + "," + m.end() + "]"); } System.out.println("===========匹配字符串結尾(多行模式)==========="); p = Pattern.compile("java$", Pattern.MULTILINE); m = p.matcher(str); while (m.find()) { System.out.println(m.group() + " 位置:[" + m.start() + "," + m.end() + "]"); } } }
===========匹配字符串開頭(非多行模式)===========
hello 位置:[0,5]
===========匹配字符串開頭(多行模式)===========
hello 位置:[0,5]
hello 位置:[13,18]
hello 位置:[25,30]
===========匹配字符串結尾(非多行模式)===========
java 位置:[31,35]
===========匹配字符串結尾(多行模式)===========
java 位置:[19,23]
java 位置:[31,35]
忽略大小寫:Pattern.CASE_INSENSITIVE 示例
有的時候,需要進行忽略大小寫的匹配。該例子實現匹配攝氏溫度和華氏溫度,對於以C、c、F和f結尾的溫度值都能匹配。
import java.util.regex.Pattern; public class ReFlags_CASE_INSENSITIVE { public static void main(String[] args) { System.out.println("===========API忽略大小寫==========="); String moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*[CF]"; Pattern p = Pattern.compile(moneyRegex,Pattern.CASE_INSENSITIVE); System.out.println("-3.33c " + p.matcher("-3.33c").matches()); System.out.println("-3.33C " + p.matcher("-3.33C").matches()); System.out.println("===========不忽略大小寫==========="); moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*[CF]"; p = Pattern.compile(moneyRegex); System.out.println("-3.33c " + p.matcher("-3.33c").matches()); System.out.println("-3.33C " + p.matcher("-3.33C").matches()); System.out.println("===========正則內部忽略大小寫==========="); moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*(?i)[CF]"; p = Pattern.compile(moneyRegex); System.out.println("-3.33c " + p.matcher("-3.33c").matches()); System.out.println("-3.33C " + p.matcher("-3.33C").matches()); System.out.println("===========內部不忽略大小寫==========="); moneyRegex = "[+-]?(\\d)+(.(\\d)*)?(\\s)*[CF]"; p = Pattern.compile(moneyRegex); System.out.println("-3.33c " + p.matcher("-3.33c").matches()); System.out.println("-3.33C " + p.matcher("-3.33C").matches()); } }
===========API忽略大小寫===========
-3.33c true
-3.33C true
===========不忽略大小寫===========
-3.33c false
-3.33C true
===========正則內部忽略大小寫===========
-3.33c true
-3.33C true
===========內部不忽略大小寫===========
-3.33c false
-3.33C true
啟用註釋:Pattern.COMMENTS 示例
啟用註釋,開啟之後,正則表達式中的空格以及#號行將被忽略。
import java.util.regex.Pattern; public class ReFlags_COMMENTS { public static void main(String[] args) { System.out.println("===========API啟用註釋==========="); String comments = " (\\d)+#this is comments."; Pattern p = Pattern.compile(comments, Pattern.COMMENTS); System.out.println("1234 " + p.matcher("1234").matches()); System.out.println("===========不啟用註釋==========="); comments = " (\\d)+#this is comments."; p = Pattern.compile(comments); System.out.println("1234 " + p.matcher("1234").matches()); System.out.println("===========正則啟用註釋==========="); comments = "(?x) (\\d)+#this is comments."; p = Pattern.compile(comments); System.out.println("1234 " + p.matcher("1234").matches()); System.out.println("===========不啟用註釋==========="); comments = " (\\d)+#this is comments."; p = Pattern.compile(comments); System.out.println("1234 " + p.matcher("1234").matches()); } }
===========API啟用註釋===========
1234 true
===========不啟用註釋===========
1234 false
===========正則啟用註釋===========
1234 true
===========不啟用註釋===========
1234 false
可以看到,#號到行尾的註釋部分和前面的空白字符都被忽略瞭。正則表達式內置的啟用註釋為(?x)。
啟用 dotall 模式:Pattern.DOTALL 示例
啟用dotall模式,一般情況下,點號(.)匹配任意字符,但不匹配換行符,啟用這個模式之後,點號還能匹配換行符。
import java.util.regex.Pattern; public class ReFlags_DOTALL { public static void main(String[] args) { System.out.println("===========API啟用DOTALL==========="); String dotall = "<xml>(.)*</xml>"; Pattern p = Pattern.compile(dotall, Pattern.DOTALL); System.out.println("<xml>\\r\\n</xml> " + p.matcher("<xml>\r\n</xml>").matches()); System.out.println("===========不啟用DOTALL==========="); dotall = "<xml>(.)*</xml>"; p = Pattern.compile(dotall); System.out.println("<xml>\\r\\n</xml> " + p.matcher("<xml>\r\n</xml>").matches()); System.out.println("===========正則啟用DOTALL==========="); dotall = "(?s)<xml>(.)*</xml>"; p = Pattern.compile(dotall); System.out.println("<xml>\\r\\n</xml> " + p.matcher("<xml>\r\n</xml>").matches()); System.out.println("===========不啟用DOTALL==========="); dotall = "<xml>(.)*</xml>"; p = Pattern.compile(dotall); System.out.println("<xml>\\r\\n</xml> " + p.matcher("<xml>\r\n</xml>").matches()); } }
===========API啟用DOTALL===========
<xml>\r\n</xml> true
===========不啟用DOTALL===========
<xml>\r\n</xml> false
===========正則啟用DOTALL===========
<xml>\r\n</xml> true
===========不啟用DOTALL===========
<xml>\r\n</xml> false
平白字符模式 模式:Pattern.LITERAL 示例
啟用這個模式之後,所有元字符、轉義字符都被看成普通的字符,不再具有其他意義。
import java.util.regex.Pattern; public class ReFlags_LITERAL { public static void main(String[] args) { System.out.println(Pattern.compile("\\d", Pattern.LITERAL).matcher("\\d").matches());// true System.out.println(Pattern.compile("\\d", Pattern.LITERAL).matcher("2").matches());// false System.out.println(Pattern.compile("(\\d)+", Pattern.LITERAL).matcher("1234").matches());// false System.out.println(Pattern.compile("(\\d)+").matcher("1234").matches());// true System.out.println(Pattern.compile("(\\d){2,3}", Pattern.LITERAL).matcher("(\\d){2,3}").matches());// true } }
附:貪婪匹配與懶惰匹配
考慮這個表達式:a.*b,它將會匹配最長的以a開始,以b結束的字符串。如果用它來搜索aabab的話,它會匹配整個字符串aabab。這被稱為貪婪匹配。
有時,我們更需要懶惰匹配,也就是匹配盡可能少的字符。前面給出的限定符都可以被轉化為懶惰匹配模式,隻要在它後面加上一個問號?。這樣.*?就意味著匹配任意數量的重復,但是在能使整個匹配成功的前提下使用最少的重復。
a.*?b匹配最短的,以a開始,以b結束的字符串。如果把它應用於aabab的話,它會匹配aab和ab。
public static void main(String[] args) { String str = "北京市(海淀區)(朝陽區)"; String paternStr = ".*(?=\\()"; Pattern pattern = Pattern.compile(paternStr); Matcher matcher = pattern.matcher(str); if (matcher.find()) { System.out.println(matcher.group(0)); } }
上述方法的輸出為:北京市(海淀區)
public static void main(String[] args) { String str = "北京市(海淀區)(朝陽區)"; String paternStr = ".*?(?=\\()"; Pattern pattern = Pattern.compile(paternStr); Matcher matcher = pattern.matcher(str); if (matcher.find()) { System.out.println(matcher.group(0)); } }
上述方法輸出:北京市
總結
到此這篇關於Java正則表達式之Pattern類的文章就介紹到這瞭,更多相關Java正則表達式Pattern類內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java中Pattern.compile函數的使用詳解
- Java之Pattern.compile函數用法詳解
- Java Pattern與Matcher字符串匹配案例詳解
- Pattern.compile函數提取字符串中指定的字符(推薦)
- java 如何判斷是否是26個英文字母