2.5 使用正則表達式進行替換
在上一節中,介紹了用于匹配一個String中的模式和用于從一個子模式組中檢索數據的正則表達式。使用regex,還可以用新的值替代匹配的模式。完成此操作的一種方法是使用Matcher類的replaceAll方法,它將返回一個字符串,將所有匹配的子串替換為給定的字符串。為了說明此方法,查找一個文件內出現的所有repetition單詞并使用單詞duplication來替換它們:
String data = getStringData();
Pattern repPattern = Pattern.compile("(\\s)(repetition)([\\s;\\.,])");
Matcher repMatcher = repPattern.matcher(data);
String newData = repMatcher.replaceAll("$1duplication$3");
|
為了查找單詞,需要捕獲它前后的空白(或標點符號)。需要注意的是,如果此單詞出現在data字符串的開頭,那么上述的代碼不會匹配它,因為已假定它的前后存在空白符。除了單詞repetition被替換外,我們希望文本中的其他內容與原始文本保持一致,包括單詞周圍的空白字符。這里的美元符號($)顯然不表示貨幣。它表示對從regex模式中捕獲的組1和組3的逆向引用,包含最初匹配的空白或標點符號。這樣,與它們對應的值將插入到替換文本中。
String類(在JDK 1.4或更高的版本中)有一個replaceAll方法,其工作機制類似于Matcher中的replaceAll方法。這使得它可以很方便地替換一個與模式匹配的子串:
replaceAll方法返回一個新的字符串,其中所有匹配的部分已被新值替換。但是,使用Matcher仍有很多優點,因為相對于字符串它有更大的靈活性。
可以使用Matcher的find循環用新值依次替換每個匹配的部分。這樣使你能夠更好地控制替換過程。比如,可以在每次匹配的過程中應用其他邏輯,甚至每次可以替換不同的值。這就用到了StringBuffer,StringBuffer用于保存更新的文本,并且一旦調用appendReplacement方法,Matcher就追加更新后的文本到緩存中。在處理每個匹配并執行替換后,需要使用appendTail方法將最后一次匹配后的剩余字符串放置在輸出緩存中。圖2-1說明了子串匹配和這兩種方法之間的關系。
 |
圖2-1 Matcher類的append方法 |
Matcher有一個相應的append指針。指針最初從零開始,隨著每次調用appendReplacement向前移動。這種設計是為了在一個find循環內使用它。每次匹配后,調用appendReplacement方法,Matcher將指針所在的上一個位置到匹配之前指針所在的位置之間的內容,即未更改的文本合并到StringBuffer中。然后,Matcher替換當前匹配的文本并將替換后的內容放置在StringBuffer中。接下來,Matcher將append指針移動到當前匹配結尾之后的第一個字符,然后重復此過程直到不再產生匹配。在找到所有匹配之后很可能剩下一個未匹配的部分。為了將這部分文本添加到輸出StringBuffer中,使用appendTail方法。
現在使用這些方法將前面的替換例子重寫為一個循環。但是這一次對于每個匹配,將使用一個隨機選擇的同義詞(repetition、duplication、copying、reiteration、recurrence或redundancy)來替代單詞repetition:
StringBuffer result = new StringBuffer();
String[] wordChoices = new String[]
{"repetition", "duplication", "copying",
"reiteration", "recurrence", "redundancy"};
Random rand = new Random();
String data = getStringData();
Pattern repPattern = Pattern.compile("(\\s)(repetition)([\\s;\\.,])");
Matcher repMatcher = repPattern.matcher(data);
while (repMatcher.find()) {
// pick a word at random
int wordIndex = rand.nextInt(wordChoices.length);
String replacement = "$1" + wordChoices[wordIndex] + "$3";
repMatcher.appendReplacement(result, replacement);
}
repMatcher.appendTail(result);
System.out.println(result);
|
可以按需求改寫find循環中的邏輯來對每個匹配進行所需的處理。此外,還可以使用前面討論過的Matcher的方法:group、start和end。可以使用這些技術的組合有選擇地修改或刪除一個文件中每部分匹配的文本。