Java 异常java.lang.IllegalArgumentException: Illegal group reference: group index is missing

注:该文是本博主记录学习之用,没有太多详细的讲解,敬请谅解!

一、背景

想必大家在日常项目中对于字符串替换比较常用的方法都是用String类中的replace、replaceAll、replaceFirst等方法,本博主今天刚好遇到这些方法中一个小小的坑,因为平时不太注意,所以今天刚好踩雷了,所以撰写本文以做学习记录之用。

二、示例代码

Java 异常java.lang.IllegalArgumentException: Illegal group reference: group index is missing_第1张图片

以上示例代码输出结果
在这里插入图片描述
调用 replace、replaceAll、replaceFirst三个方法替换相同内容,由输出的结果可以看出,replace方法虽然成功但是没有替换到字符串,而replaceAll、replaceFirst这两个方法则抛出了异常:java.lang.IllegalArgumentException: Illegal group reference: group index is missing

既然报错了,我们就要找错,所以我们接下来分别看下这三个方法的源码处理:

  1. replace方法
    我们点进查看String类中replace的源码
    在这里插入图片描述
    从replace方法中我们会看到两个比较重要的点:
    第一个是Pattern.compile(target.toString(), Pattern.LITERAL),这里的正则匹配Pattern.LITERAL的意思就是:启用字面值解析模式, 指定此标志后,指定模式的输入字符串就会作为字面值字符序列来对待,输入序列中的元字符或转义序列不具有任何特殊意义(通俗的说就是输入值是什么就是什么)

    第二个是Matcher.quoteReplacement(replacement.toString()),这个是重点,我们跳到Matcher类查看quoteReplacement方法,这个方法主要是处理特殊符号$和\,为它俩加上转义符号
    Java 异常java.lang.IllegalArgumentException: Illegal group reference: group index is missing_第2张图片
    由此我们可以看得出为什么replace方法可以成功,但是却没有替换值,要想replace成功替换内容需要改成str.replace("?",replacement),不加任何转义符号。

  2. replaceAll和replaceFirst方法
    为什么这里要一起讲这两个方法,因为这两个方法抛出异常的处理逻辑是一样的,我们首先看下它们的源码,由String类中的replaceAll和replaceFirst方法中跳转到Matcher类中的replaceAll和replaceFirst方法,以下是Matcher类中的方法
    Java 异常java.lang.IllegalArgumentException: Illegal group reference: group index is missing_第3张图片
    Java 异常java.lang.IllegalArgumentException: Illegal group reference: group index is missing_第4张图片
    从上面两个方法中你可以看到它们都调用了一个appendReplacement方法,正是这个方法抛出了异常,接下来看appendReplacement方法的源码(图比较长,所以这里用代码显示)

public Matcher appendReplacement(StringBuffer sb, String replacement) {

        // If no match, return error
        if (first < 0)
            throw new IllegalStateException("No match available");

        // Process substitution string to replace group references with groups
        int cursor = 0;
        StringBuilder result = new StringBuilder();

        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 == '$') {
                // Skip past $
                cursor++;
                // Throw IAE if this "$" is the last character in replacement
                if (cursor == replacement.length())
                   throw new IllegalArgumentException(
                        "Illegal group reference: group index is missing");
                nextChar = replacement.charAt(cursor);
                int refNum = -1;
                if (nextChar == '{') {
                    cursor++;
                    StringBuilder gsb = new StringBuilder();
                    while (cursor < replacement.length()) {
                        nextChar = replacement.charAt(cursor);
                        if (ASCII.isLower(nextChar) ||
                            ASCII.isUpper(nextChar) ||
                            ASCII.isDigit(nextChar)) {
                            gsb.append(nextChar);
                            cursor++;
                        } else {
                            break;
                        }
                    }
                    if (gsb.length() == 0)
                        throw new IllegalArgumentException(
                            "named capturing group has 0 length name");
                    if (nextChar != '}')
                        throw new IllegalArgumentException(
                            "named capturing group is missing trailing '}'");
                    String gname = gsb.toString();
                    if (ASCII.isDigit(gname.charAt(0)))
                        throw new IllegalArgumentException(
                            "capturing group name {" + gname +
                            "} starts with digit character");
                    if (!parentPattern.namedGroups().containsKey(gname))
                        throw new IllegalArgumentException(
                            "No group with name {" + gname + "}");
                    refNum = parentPattern.namedGroups().get(gname);
                    cursor++;
                } else {
                    // The first number is always a group
                    refNum = (int)nextChar - '0';
                    if ((refNum < 0)||(refNum > 9))
                        throw new IllegalArgumentException(
                            "Illegal group reference");
                    cursor++;
                    // Capture the largest legal group string
                    boolean done = false;
                    while (!done) {
                        if (cursor >= replacement.length()) {
                            break;
                        }
                        int nextDigit = replacement.charAt(cursor) - '0';
                        if ((nextDigit < 0)||(nextDigit > 9)) { // not a number
                            break;
                        }
                        int newRefNum = (refNum * 10) + nextDigit;
                        if (groupCount() < newRefNum) {
                            done = true;
                        } else {
                            refNum = newRefNum;
                            cursor++;
                        }
                    }
                }
                // Append group
                if (start(refNum) != -1 && end(refNum) != -1)
                    result.append(text, start(refNum), end(refNum));
            } 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;
    }

通过以上代码分析,可以看到这里面对 $ 符号和 \\ 符号进行了处理。出现异常错误的原因是:如果参数replacement中出现这两个符号,$符号处理会按照 $1的分组模式进行匹配。当编译器发现$后跟的不是整数的时候,就会抛出“Illegal group reference”的异常。 \\ 符号处理如果长度相等则也会抛出异常。

三、最终正常处理结果

既然知道了它抛出异常的原因,所以我们就更好的处理,我们前面也讲到了replace中调用到了Matcher类中quoteReplacement的方法进行转义,所以它能正常替换,因为replace是自动加上这个方法,所以不用做处理,调用replaceAll和replaceFirst方法时如果涉及到这两个特殊符号就要加上quoteReplacement进行转义。最终正常处理(如下图):

在这里插入图片描述在这里插入图片描述

你可能感兴趣的:(Java)