赞
踩
注:该文是本博主记录学习之用,没有太多详细的讲解,敬请谅解!
想必大家在日常项目中对于字符串替换比较常用的方法都是用String类中的replace、replaceAll、replaceFirst等方法,本博主今天刚好遇到这些方法中一个小小的坑,因为平时不太注意,所以今天刚好踩雷了,所以撰写本文以做学习记录之用。
以上示例代码输出结果
调用 replace、replaceAll、replaceFirst三个方法替换相同内容,由输出的结果可以看出,replace方法虽然成功但是没有替换到字符串,而replaceAll、replaceFirst这两个方法则抛出了异常:java.lang.IllegalArgumentException: Illegal group reference: group index is missing
既然报错了,我们就要找错,所以我们接下来分别看下这三个方法的源码处理:
replace方法
我们点进查看String类中replace的源码
从replace方法中我们会看到两个比较重要的点:
第一个是Pattern.compile(target.toString(), Pattern.LITERAL),这里的正则匹配Pattern.LITERAL的意思就是:启用字面值解析模式, 指定此标志后,指定模式的输入字符串就会作为字面值字符序列来对待,输入序列中的元字符或转义序列不具有任何特殊意义(通俗的说就是输入值是什么就是什么)
第二个是Matcher.quoteReplacement(replacement.toString()),这个是重点,我们跳到Matcher类查看quoteReplacement方法,这个方法主要是处理特殊符号$和\,为它俩加上转义符号
由此我们可以看得出为什么replace方法可以成功,但是却没有替换值,要想replace成功替换内容需要改成str.replace("?",replacement),不加任何转义符号。
replaceAll和replaceFirst方法
为什么这里要一起讲这两个方法,因为这两个方法抛出异常的处理逻辑是一样的,我们首先看下它们的源码,由String类中的replaceAll和replaceFirst方法中跳转到Matcher类中的replaceAll和replaceFirst方法,以下是Matcher类中的方法
从上面两个方法中你可以看到它们都调用了一个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进行转义。最终正常处理(如下图):
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。