關于Java中Match類的appendReplacement()方法的一個坑{java.lang.IllegalArgumentException: character to be escaped is missing}
問題描述
在向替換結果中追加資訊的時候,調用match.appendReplacement(buffer,str)時出現了上述異常。
class XX{
public String renderString(String source, Map<String, Object> context, Map<String, Object> data) throws OgnlException {
Pattern pattern = Pattern.compile(DELIM);
Matcher matcher = pattern.matcher(source);
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
String e = matcher.group();
if (e == null) throw new NullPointerException("expression can not be null");
Object value = Ognl.getValue(e, context, data);
String str = null == value ? "null" : value.toString();
matcher.appendReplacement(buffer, str); //在此處報錯
}
matcher.appendTail(buffer);
return buffer.toString();
}
}
問題原因
通過閱讀Matcher類的源碼并查閱文檔得知,在 matcher.appendReplacement(buffer, str)方法中str中若含有'\'、'$'字元時,将存在特殊含義,
'$n'代表比對的第n組結果,'\'将對其之後的字元進行轉義。
文檔如下
實作非終端添加和替換步驟。
此方法執行以下操作:
它從添加位置開始在輸入序列讀取字元,并将其添加到給定字元串緩沖區。在讀取以前比對之前的最後字元(即位于索引 start() - 處的字元)之後,它就會停止。
它将給定替換字元串添加到字元串緩沖區。
它将此比對器的添加位置設定為最後比對位置的索引加 ,即 end()。
替換字元串可能包含到以前比對期間所捕獲的子序列的引用:$g 每次出現時,都将被 group(g) 的計算結果替換。$ 之後的第一個數始終被視為組引用的一部分。如果後續的數可以形成合法組引用,則将被合并到 g 中。隻有數字 '0' 到 '9' 被視為組引用的可能元件。例如,如果第二個組比對字元串 "foo",則傳遞替換字元串 "$2bar" 将導緻 "foobar" 被添加到字元串緩沖區。可能将美元符号 ($) 作為替換字元串中的字面值(通過前面使用一個反斜線 (\$))包括進來。
注意,在替換字元串中使用反斜線 (\) 和美元符号 ($) 可能導緻與作為字面值替換字元串時所産生的結果不同。美元符号可視為到如上所述已捕獲子序列的引用,反斜線可用于轉義替換字元串中的字面值字元。
此方法設計用于循環以及 appendTail 和 find 方法中。例如,以下代碼将 one dog two dogs in the yard 寫入标準輸出流中:
Pattern p = Pattern.compile("cat");
Matcher m = p.matcher("one cat two cats in the yard");
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, "dog");
}
m.appendTail(sb);
System.out.println(sb.toString());
參數:
sb - 目标字元串緩沖區。
replacement - 替換字元串。
傳回:
比對器。
抛出:
IllegalStateException - 如果沒有嘗試任何比對,或者以前的比對操作失敗。
IndexOutOfBoundsException - 如果替換字元串引用模式中不存在的捕獲組。
源碼如下
public final class Matcher implements MatchResult {
//省略
public Matcher appendReplacement(StringBuffer sb, String replacement) {
//省略
while (cursor < replacement.length()) {
char nextChar = replacement.charAt(cursor);
if (nextChar == '\\') {
cursor++;
if (cursor == replacement.length())
throw new IllegalArgumentException(
"character to be escaped is missing");
nextChar = replacement.charAt(cursor);
result.append(nextChar);
cursor++;
} else if (nextChar == '$') {
//省略
} else {
result.append(nextChar);
cursor++;
}
}
// Append the intervening text
sb.append(text, lastAppendPosition, first);
// Append the match substitution
sb.append(result);
lastAppendPosition = last;
return this;
}
}