Matcher 物件
July 2, 2022在建立 Pattern
實例之後,可以使用 matcher
方法指定要比對的字串,這會傳回 java.util.regex.Matcher
實例,表示對指定字串的比對器。
Matcher 的方法
可以使用 find
方法來測試是否有下個符合字串,或是使用 lookingAt
看看字串開頭是否符合規則表示式,使用 group
方法則可以傳回符合的字串。例如:
package cc.openhome;
import static java.lang.System.out;
import java.util.regex.*;
public class MatcherDemo {
public static void main(String[] args) {
var regexs = {".*foo", ".*?foo", ".*+foo"};
for(String regex : regexs) {
var pattern = Pattern.compile(regex);
var matcher = pattern.matcher("xfooxxxxxxfoo");
out.printf("%s find ", pattern.pattern());
while(matcher.find()) {
out.printf(" \"%s\"", matcher.group());
}
out.println(" in \"xfooxxxxxxfoo\".");
}
}
}
這個範例示範了貪婪、逐步與獨吐量詞的比對結果,執行結果如下:
.*foo find "xfooxxxxxxfoo" in "xfooxxxxxxfoo".
.*?foo find "xfoo" "xxxxxxfoo" in "xfooxxxxxxfoo".
.*+foo find in "xfooxxxxxxfoo".
如果規則表示式中有分組,group
可以接受 int
整數指定分數計數,舉例來說,規則表示式如果是 ((A)(B(C)))
,若指定文字為 ABC,matcher.find
後指定 group(1)
就是 "ABC"
,group(2)
就是 "A"
、group(3)
就是 "BC"
,group(4)
就是 "C"
,由於分組計數會從 1 開始,因此 group(0)
就相當於直接呼叫沒有參數的 group()
。
如果設定了命名分組,group
方法可以指定名稱來取得分組:
jshell> var regex = Pattern.compile("(?<user>^[a-zA-Z]+\\d*)@(?<preCom>[a-z]+?.)com");
regex ==> (?<user>^[a-zA-Z]+\d*)@(?<preCom>[a-z]+?.)com
jshell> var matcher = regex.matcher("caterpillar@openhome.com");
matcher ==> java.util.regex.Matcher[pattern=(?<user>^[a-zA-Z] ... om region=0,24 lastmatch=]
jshell> matcher.find();
$3 ==> true
jshell> matcher.group("user");
$4 ==> "caterpillar"
jshell> matcher.group("preCom");
$5 ==> "openhome."
Matcher
還有 replaceAll
方法,可以將符合規則表示式的部份以指定的字串取代,效果等同於 String
的 replaceAll
方法,replaceFirst
與 replaceEnd
可分別取代首個、最後符合規則表示式的部份;start
方法可以取得符合字串的起始索引,end
方法可取得符合字串最後一個字元後的索引。
分組設定
如果規則表示中有分組設定,在使用 replaceAll
時,可以使用 $n
來捕捉被分組匹配的文字(使用於 String
的 replaceAll
、replaceFirst
也可以)。
例如,以下的片段可以將使用者郵件位址從 .com 取代為 .cc:
var pattern = Pattern.compile("(^[a-zA-Z]+\\d*)@([a-z]+?.)com");
var matcher = pattern.matcher("caterpillar@openhome.com");
out.println(matcher.replaceAll("$1@$2cc")); // caterpillar@openhome.cc
如果是命名分組,使用的是 ${name}
形式:
jshell> var regex = Pattern.compile("(?<user>^[a-zA-Z]+\\d*)@(?<preCom>[a-z]+?.)com");
regex ==> (?<user>^[a-zA-Z]+\d*)@(?<preCom>[a-z]+?.)com
jshell> var matcher = regex.matcher("caterpillar@openhome.com");
matcher ==> java.util.regex.Matcher[pattern=(?<user>^[a-zA-Z] ... om region=0,24 lastmatch=]
jshell> matcher.replaceAll("${user}@${preCom}cc");
$8 ==> "caterpillar@openhome.cc"
函數式概念 API
Matcher
的狀態顯然是可變的,如果目前狀態取得的比對結果,不想被後續比對影響,可以使用 toMatcherResult
方法取得 MatcherResult
實作物件,傳回的物件是不可變、只包含該次比對狀態;實際上,Matcher
也實作了 MatcherResult
介面。
replaceAll
、replaceFirst
有可以接受 Function<MatchResult,String>
的版本,因此可以自訂取代函式:
jshell> var regex = Pattern.compile("(^[a-zA-Z]+\\d*)@([a-z]+?.)com");
regex ==> (^[a-zA-Z]+\d*)@([a-z]+?.)com
jshell> var matcher = regex.matcher("caterpillar@openhome.com");
matcher ==> java.util.regex.Matcher[pattern=(^[a-zA-Z]+\d*)@( ... om region=0,24 lastmatch=]
jshell> matcher.replaceAll(result -> String.format("%s@%scc", result.group(1), result.group(2)));
$11 ==> "caterpillar@openhome.cc"
另外還有個 results
方法,可傳回 Stream<MatchResult>
實例,便於透過 Stream API 操作:
jshell> var matcher = regex.matcher("Justin+Monica+Irene");
matcher ==> java.util.regex.Matcher[pattern=\pL+ region=0,19 lastmatch=]
jshell> matcher.results().map(result -> result.group().toUpperCase()).forEach(out::println);
JUSTIN
MONICA
IRENE