天天看點

關于Java中Match類的appendReplacement()方法的一個坑{ character to be escaped }關于Java中Match類的appendReplacement()方法的一個坑{java.lang.IllegalArgumentException: character to be escaped is missing}

關于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;
    }
}