2.4 使用Regex捕獲組
在上一節(jié)中,介紹了如何使用正則表達(dá)式在一個文件中進(jìn)行搜索以便檢索它內(nèi)部所有的URL。可以使用Matcher類的find、start和end方法來檢索匹配的URL字符串。有時有必要進(jìn)一步處理子串匹配的結(jié)果,或是查找附加的子模式。例如,對某個特定區(qū)域的URL不進(jìn)行處理。為了實現(xiàn)此目的,一種強(qiáng)制性的方法是使用另一個Pattern和Matcher對象,代碼如下:
// assume urlMatcher instance as in the previous example
while (urlMatcher.find()) {
int startIndex = urlMatcher.start();
int endIndex = urlMatcher.end();
String currentMatch = data.substring(startIndex, endIndex);
// the brute force approach, using a new pattern!
Pattern restricted = Pattern.compile(".*(abc|cbs|nbc)\\.com.*");
Matcher restrictMatcher = restricted.matcher(currentMatch);
if (!restrictMatcher.matches()) {
System.out.println(currentMatch);
}
}
|
在捕獲的URL中匹配域名并不是一個非常高效的方法。由于已經(jīng)使用find方法完成了提取URL的困難工作,不應(yīng)該僅僅為了獲得結(jié)果的一部分而編寫另一個regex,并且也不必這樣做。正則表達(dá)式允許將模式分解成子序列。使用圓括號,將以后要用到的模式部分括起來,這樣,我們就可以忽略其余部分單獨(dú)讀取這些部分的值。重寫URL模式以使域名可以與URL的其他部分相分離:
String urlPattern =
"(http|https|ftp)://([a-zA-Z0-9-\\.]+)[/\\w\\.\\-\\+\\?%=&;:,#]*";
|
當(dāng)在模式中存在用括號括起來的組時,可以分別檢索每個組的匹配值。從最左邊的組開始編為1,然后依次對每對括號相對應(yīng)的組進(jìn)行編號。在上面的模式中,第一組是協(xié)議(如http),第二組是域名。為了在匹配的字符串中訪問組,可以使用Matcher的group方法。下面的代碼示例從每個URL中檢索域名并顯示它們的值:
String data = getStringData(); // load the document
String urlString =
"(http|https|ftp)://([a-zA-Z0-9-\\.]+)[/\\w\\.\\-\\+\\?%=&;:,#]*";
Pattern urlPattern = Pattern.compile(urlString);
Matcher urlMatcher = urlPattern.matcher(data);
// print out the domain from each URL
while (urlMatcher.find()) {
String domain = urlMatcher.group(2); // 2nd group is the domain
System.out.println(domain);
}
|
保存每個匹配的組以便可以隨后引用它們。在一個模式內(nèi)引用一個以前的匹配組稱為逆向引用(backreference)。為了對第三個組進(jìn)行逆向引用,在模式中包括\3即可。這將會只匹配一個與以前的組相匹配的嚴(yán)格重復(fù)的數(shù)據(jù)。為了說明此問題,考慮一個在文本文件中常見的錯誤—— 一個句子中意外地重復(fù)出現(xiàn)某個常用的單詞,如“the”或“of”。
" The the water molecules are made of of hydrogen and oxygen."
|
下面編寫一個模式來找出文件中存在的這些問題。該模式將捕獲第一個單詞,后跟一些空白符,而其后又跟著匹配第一個單詞的重復(fù)模式:
該模式匹配情況如下:一個空白字符、特殊的單詞列表中的一個單詞、更多的空白、再次重復(fù)的相同的單詞(使用\1逆向引用)以及空白符或標(biāo)點(diǎn)符號。這種匹配應(yīng)不區(qū)分大小寫,以便能夠捕獲到“The the”以及類似的變型。如以下的代碼段所示,該模式不區(qū)分大小寫,能在一個字符串中查找重復(fù)出現(xiàn)的模式:
String data = getStringData();
String patternStr = "\\s(of|or|the|to)\\s+\\1[\\s\\.,;]";
Pattern wordPattern =
Pattern.compile(patternStr, Pattern.CASE_INSENSITIVE);
Matcher wordMatcher = wordPattern.matcher(data);
while (wordMatcher.find()) {
int start = wordMatcher.start();
String word = wordMatcher.group(1);
// print the index location of the repeated word
System.out.println("Repeated " + word + " starting at " + start);
}
|
有一種簡便和強(qiáng)大的匹配文件中文本的方法,該方法允許使用多個正則表達(dá)式來處理文件,本章后面的“使用Scanner類進(jìn)行語法分析”一節(jié)將會講解此方法。若想了解使用內(nèi)置索引進(jìn)行更為復(fù)雜的文本搜索的解決方法,請參考第3章中“使用Lucene進(jìn)行搜索”一節(jié)的內(nèi)容。