Java Pattern和Matcher字元匹配詳解

Pattern類定義

         public final class Pattern extends Object implementsSerializable正規表示式的編譯表示形式。用於編譯正規表示式後建立一個匹配模式。

        指定為字串的正規表示式必須首先被編譯為此類的例項。然後,可將得到的模式用於建立Matcher物件,依照正規表示式,該物件可以與任意字元序列匹配。執行匹配所涉及的所有狀態都駐留在匹配器中,所以多個匹配器可以共享同一模式。

        因此,典型的呼叫順序是:

        Pattern p =Pattern.compile(“a*b”);

        Matcher m =p.matcher(“aaaaab”);

        boolean b = m.matches();

        在僅使用一次正規表示式時,可以方便地通過此類定義matches方法。此方法編譯表示式並在單個呼叫中將輸入序列與其匹配。語句:

        boolean b =Pattern.matches(“a*b”, “aaaaab”);

        等效於上面的三個語句,儘管對於重複的匹配而言它效率不高,因為它不允許重用已編譯的模式。

        此類的例項是不可變的,可供多個併發執行緒安全使用。Matcher類的例項用於此目的則不安全。

Pattern類方法詳解

        1、Pattern complie(String regex):編譯正規表示式,並建立Pattern類。

        由於Pattern的建構函式是私有的,不可以直接建立,所以通過靜態的簡單工廠方法compile(String regex)方法來建立,將給定的正規表示式編譯並賦予給Pattern類。

        2、String pattern():返回正規表示式的字串形式。

        其實就是返回Pattern.complile(Stringregex)的regex引數。示例如下:

        String regex =”\\?|\\*”;

        Pattern pattern= Pattern.compile(regex);

        StringpatternStr = pattern.pattern();//返回\?\*

        3、Pattern compile(String regex, int flags)。

        方法功能和compile(Stringregex)相同,不過增加了flag引數,flag引數用來控制正規表示式的匹配行為,可取值範圍如下:

        Pattern.CANON_EQ:啟用規範等價。當且僅當兩個字元的“正規分解(canonicaldecomposition)”都完全相同的情況下,才認定匹配。預設情況下,不考慮“規範相等性(canonical equivalence)”。

        Pattern.CASE_INSENSITIVE:啟用不區分大小寫的匹配。預設情況下,大小寫不敏感的匹配只適用於US-ASCII字符集。這個標誌能讓表示式忽略大小寫進行匹配,要想對Unicode字元進行大小不敏感的匹配,只要將UNICODE_CASE與這個標誌合起來就行了。

        Pattern.COMMENTS:模式中允許空白和註釋。在這種模式下,匹配時會忽略(正規表示式裡的)空格字元(不是指表示式裡的“\s”,而是指表示式裡的空格,tab,回車之類)。註釋從#開始,一直到這行結束。可以通過嵌入式的標誌來啟用Unix行模式。

        Pattern.DOTALL:啟用dotall模式。在這種模式下,表示式‘.’可以匹配任意字元,包括表示一行的結束符。預設情況下,表示式‘.’不匹配行的結束符。

        Pattern.LITERAL:啟用模式的字面值解析。

        Pattern.MULTILINE:啟用多行模式。在這種模式下,‘\^’和‘$’分別匹配一行的開始和結束。此外,‘^’仍然匹配字串的開始,‘$’也匹配字串的結束。預設情況下,這兩個表示式僅僅匹配字串的開始和結束。

        Pattern.UNICODE_CASE:啟用Unicode感知的大小寫摺疊。在這個模式下,如果你還啟用了CASE_INSENSITIVE標誌,那麼它會對Unicode字元進行大小寫不敏感的匹配。預設情況下,大小寫不敏感的匹配只適用於US-ASCII字符集。

        Pattern.UNIX_LINES:  啟用Unix行模式。在這個模式下,只有‘\n’才被認作一行的中止,並且與‘.’、‘^’、以及‘$’進行匹配。

        4、int flags():返回當前Pattern的匹配flag引數。

        5、String[] split(CharSequence input)。

        Pattern有一個split(CharSequenceinput)方法,用於分隔字串,並返回一個String[]。此外String[] split(CharSequence input, int limit)功能和String[]split(CharSequence input)相同,增加引數limit目的在於要指定分割的段數。

        6、static boolean matches(String regex, CharSequenceinput)。

        是一個靜態方法,用於快速匹配字串,該方法適合用於只匹配一次,且匹配全部字串。方法編譯給定的正規表示式並且對輸入的字串以該正規表示式為模式開展匹配,該方法只進行一次匹配工作,並不需要生成一個Matcher例項。

        7、Matcher matcher(CharSequence input)。

        Pattern.matcher(CharSequenceinput)返回一個Matcher物件。Matcher類的構造方法也是私有的,不能隨意建立,只能通過Pattern.matcher(CharSequence input)方法得到該類的例項。Pattern類只能做一些簡單的匹配操作,要想得到更強更便捷的正則匹配操作,那就需要將Pattern與Matcher一起合作。Matcher類提供了對正規表示式的分組支援,以及對正規表示式的多次匹配支援。

        Java程式碼示例:

        Pattern p = Pattern.compile(“\\d “);

        Matcher m = p.matcher(“22bb23”);

        // 返回p也就是返回該Matcher物件是由哪個Pattern物件的建立的

        m.pattern();

Pattern類使用示例:

package com.zxt.regex;
import java.util.regex.Pattern;
public classPatternTest {
public static void main(String[] args) {
// 使用Pattern.compile方法編譯一個正規表示式,建立一個匹配模式
Patternpattern = Pattern.compile("\\?|\\*");
// pattern()函式返回正規表示式的字串形式返回\?\*
StringpatternStr = pattern.pattern();
System.out.println(patternStr);
// flags()返回當前Pattern的匹配flag引數,這裡並沒有定義
int flag = pattern.flags();
System.out.println(flag);
// split方法對字串進行分割
// 123 123 456 456
String[]splitStrs = pattern.split("123?123*456*456");
for (int i = 0; i < splitStrs.length; i  ) {
System.out.print(splitStrs[i]   "  ");
}
System.out.println();
// 123 123*456*456
String[]splitStrs2 = pattern.split("123?123*456*456",2);
for (int i = 0; i < splitStrs2.length; i  ) {
System.out.print(splitStrs2[i]   "  ");
}
System.out.println();
Patternp = Pattern.compile("\\d ");
String[]str = p.split("我的QQ是:456456我的電話是:0532214我的郵箱是:[email protected]");
for (int i = 0; i < str.length; i  ) {
System.out.printf("str[%d] = %s\n",i, str[i]);
}
System.out.println();
// Pattern.matches用給定的模式對字串進行一次匹配,(需要全匹配時才返回true)
System.out.println("Pattern.matches(\"\\\\d \",\"2223\") is "   Pattern.matches("\\d ", "2223"));
// 返回false,需要匹配到所有字串才能返回true,這裡aa不能匹配到
System.out.println("Pattern.matches(\"\\\\d \", \"2223aa\")is "   Pattern.matches("\\d ", "2223aa"));
// 返回false,需要匹配到所有字串才能返回true,這裡bb不能匹配到
System.out.println("Pattern.matches(\"\\\\d \",\"22bb23\") is "   Pattern.matches("\\d ", "22bb23"));
}
}

Matcher類定義

        public final class Matcher extends Object implementsMatchResult通過呼叫模式(Pattern)的matcher方法從模式建立匹配器。建立匹配器後,可以使用它執行三種不同的匹配操作:

        1、matches方法嘗試將整個輸入序列與該模式匹配。

        2、lookingAt嘗試將輸入序列從頭開始與該模式匹配。

        3、find方法掃描輸入序列以查詢與該模式匹配的下一個子序列。

        每個方法都返回一個表示成功或失敗的布林值。通過查詢匹配器的狀態可以獲取關於成功匹配的更多資訊。

        匹配器在其輸入的子集(稱為區域)中查詢匹配項。預設情況下,此區域包含全部的匹配器輸入。可通過region方法修改區域,通過regionStart和regionEnd方法查詢區域。區域邊界與某些模式構造互動的方式是可以更改的。

        此類還定義使用新字串替換匹配子序列的方法,需要時,可以從匹配結果計算出新字串的內容。可以先後使用appendReplacement和appendTail方法將結果收集到現有的字串緩衝區,或者使用更加便捷的replaceAll方法建立一個可以在其中替換輸入序列中每個匹配子序列的字串。

        匹配器的顯式狀態包括最近成功匹配的開始和結束索引。它還包括模式中每個捕獲組捕獲的輸入子序列的開始和結束索引以及該子序列的總數。出於方便的考慮,還提供了以字串的形式返回這些已捕獲子序列的方法。

        匹配器的顯式狀態最初是未定義的;在成功匹配導致IllegalStateException丟擲之前嘗試查詢其中的任何部分。每個匹配操作都將重新計算匹配器的顯式狀態。匹配器的隱式狀態包括輸入字元序列和新增位置,新增位置最初是零,然後由appendReplacement方法更新。

        可以通過呼叫匹配器的reset()方法來顯式重置匹配器,如果需要新輸入序列,則呼叫其reset(CharSequence)方法。重置匹配器將放棄其顯式狀態資訊並將新增位置設定為零。

        此類的例項用於多個併發執行緒是不安全的。

Matcher類方法詳解

        1、Matcher類提供了三個匹配操作方法,三個方法均返回boolean型別,當匹配到時返回true,沒匹配到則返回false。

        boolean matches()最常用方法:嘗試對整個目標字元展開匹配檢測,也就是隻有整個目標字串完全匹配時才返回真值。

        boolean lookingAt()對前面的字串進行匹配,只有匹配到的字串在最前面才會返回true。

        boolean find():對字串進行匹配,匹配到的字串可以在任何位置。

        2、返回匹配器的顯示狀態:intstart():返回當前匹配到的字串在原目標字串中的位置;int end():返回當前匹配的字串的最後一個字元在原目標字串中的索引位置;String group():返回匹配到的子字串。

        3、int start(),int end(),int group()均有一個過載方法,它們分別是int start(int i),int end(int i),int group(int i)專用於分組操作,Mathcer類還有一個groupCount()用於返回有多少組。

        4、Matcher類同時提供了四個將匹配子串替換成指定字串的方法:

        1)、String replaceAll(Stringreplacement):將目標字串裡與既有模式相匹配的子串全部替換為指定的字串。

        2)、String replaceFirst(Stringreplacement):將目標字串裡第一個與既有模式相匹配的子串替換為指定的字串。

        3)、還有兩個方法Matcher appendReplacement(StringBuffersb, String replacement)
和StringBufferappendTail(StringBuffer sb)也很重要,appendReplacement允許直接將匹配的字串儲存在另一個StringBuffer中並且是漸進式匹配,並不是只匹配一次或匹配全部,而appendTail則是將未匹配到的餘下的字串新增到StringBuffer中。

        5、其他一些方法:例如Matcherreset():重設該Matcher物件。

        Matcher reset(CharSequence input):重設該Matcher物件並且指定一個新的目標字串。

        Matcher region(int start, int end):設定此匹配器的區域限制。

 

Matcher類使用示例:

package com.zxt.regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public classMatcherTest {
public static void main(String[] args) {
Patternp = Pattern.compile("\\d ");
// matches()對整個字串進行匹配
// 返回false,因為bb不能被\d 匹配,導致整個字串匹配未成功。
Matcherm = p.matcher("22bb23");
System.out.println(m.matches());
m = p.matcher("2223");
// 返回true,因為\d 匹配到了整個字串
System.out.println(m.matches());
// lookingAt()對字串字首進行匹配
m = p.matcher("22bb23");
// 返回true,因為\d 匹配到了前面的22
System.out.println(m.lookingAt());
m = p.matcher("aa2223");
// 返回false,因為\d 不能匹配前面的aa
System.out.println(m.lookingAt());
// find()對字串進行匹配,匹配到的字串可以在任何位置。
m = p.matcher("22bb23");
System.out.println(m.find()); // true
m = p.matcher("aa2223");
System.out.println(m.find()); // true
m = p.matcher("aabb");
System.out.println(m.find()); // false
// 當匹配器匹配失敗時,使用返回匹配器狀態的方法將出錯,例如:m.start();
m = p.matcher("aa2223bb");
System.out.println(m.find()); // true
System.out.println(m.start()); // 2
System.out.println(m.end()); // 6
System.out.println(m.group()); // 2223
p = Pattern.compile("([a-z] )(\\d )");
m = p.matcher("aaa2223bb");
// 匹配aaa2223
m.find();
// 返回2,因為有2組
System.out.println(m.groupCount());
// 返回0, 返回第一組匹配到的子字串在字串中的索引號
System.out.println(m.start(1));
// 返回3
System.out.println(m.start(2));
// 返回3 返回第一組匹配到的子字串的最後一個字元在字串中的索引位置.
System.out.println(m.end(1));
// 返回2223,返回第二組匹配到的子字串
System.out.println(m.group(2));
}
}

應用例項

1、一個簡單的郵箱驗證小程式

package com.zxt.regex;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* 一個簡單的郵件地址匹配程式
*/
public classEmailMatch {
public static void main(String[] args) throws Exception {
Scannersc = new Scanner(System.in);
while (sc.hasNext()) {
Stringinput = sc.nextLine();
// 檢測輸入的EMAIL地址是否以非法符號"."或"@"作為起始字元
Patternp = Pattern.compile("^@");
Matcherm = p.matcher(input);
if (m.lookingAt()) {
System.out.println("EMAIL地址不能以'@'作為起始字元");
}
// 檢測是否以"www."為起始
p = Pattern.compile("^www.");
m = p.matcher(input);
if (m.lookingAt()) {
System.out.println("EMAIL地址不能以'www.'起始");
}
// 檢測是否包含非法字元
p = Pattern.compile("[^[email protected]_-~#] ");
m = p.matcher(input);
StringBuffersb = new StringBuffer();
boolean result = m.find();
boolean deletedIllegalChars= false;
while (result) {
// 如果找到了非法字元那麼就設下標記
deletedIllegalChars= true;
// 如果裡面包含非法字元如冒號雙引號等,那麼就把他們消去,加到SB裡面
m.appendReplacement(sb, "");
result = m.find();
}
// 此方法從新增位置開始從輸入序列讀取字元,並將其新增到給定字串緩衝區。
// 可以在一次或多次呼叫 appendReplacement 方法後呼叫它來複制剩餘的輸入序列。
m.appendTail(sb);
if (deletedIllegalChars){
System.out.println("輸入的EMAIL地址裡包含有冒號、逗號等非法字元,請修改");
System.out.println("您現在的輸入為: "   input);
System.out.println("修改後合法的地址應類似: "   sb.toString());
}
}
sc.close();
}
}

2、判斷身份證:要麼是15位,要麼是18位,最後一位可以為字母,並寫程式提出其中的年月日。

        可以使用正規表示式來定義複雜的字串格式:(\d{17}[0-9a-zA-Z]|\d{14}[0-9a-zA-Z])可以用來判斷是否為合法的15位或18位身份證號碼。因為15位和18位的身份證號碼都是從7位到第12位為身份證為日期型別。這樣我們可以設計出更精確的正則模式,提取身份證號中的日期資訊。

package com.zxt.regex;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public classIdentityMatch {
public static void main(String[] args) {
// 測試是否為合法的身份證號碼
String[]id_cards = { "130681198712092019","13068119871209201x","13068119871209201","123456789012345",
"12345678901234x","1234567890123"};
// 測試是否為合法身份證的正規表示式
Patternpattern = Pattern.compile("(\\d{17}[0-9a-zA-Z]|\\d{14}[0-9a-zA-Z])");
// 用於提取出生日字串的正規表示式
Patternpattern1 = Pattern.compile("\\d{6}(\\d{8}).*");
// 用於將生日字串分解為年月日的正規表示式
Patternpattern2 = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})");
Matchermatcher = pattern.matcher("");
for (int i = 0; i < id_cards.length; i  ) {
matcher.reset(id_cards[i]);
System.out.println(id_cards[i]   " is id cards:"   matcher.matches());
// 如果它是一個合法的身份證號,提取出出生的年月日
if (matcher.matches()) {
Matchermatcher1 = pattern1.matcher(id_cards[i]);
matcher1.lookingAt();
Stringbirthday = matcher1.group(1);
Matchermatcher2 = pattern2.matcher(birthday);
if (matcher2.find()) {
System.out.println("它對應的出生年月日為:"   matcher2.group(1)   "年"   matcher2.group(2)   "月"
matcher2.group(3)   "日");
}
}
System.out.println();
}
}
}