在这个答案中,我建议使用
s.replaceFirst("\\.0*$|(\\.\\d*?)0+$", "$1");
但是两个人抱怨结果包含字符串“ null”,例如23.null。这可以通过$1(即group(1))being 来解释null,可以通过转换String.valueOf为字符串“ null”。但是,我总是得到空字符串。我的测试用例涵盖了
23.null
$1
group(1)
null
String.valueOf
assertEquals("23", removeTrailingZeros("23.00"));
通过。确切的行为是否未定义?
当替换字符串中指定了一个不捕获任何内容的捕获组()时,参考实现中的Matcher类的文档未指定appendReplacement方法的行为null。尽管group方法的行为很明确,但方法中没有任何提及appendReplacement。
appendReplacement
group
以下是上述情况在实施上的3种不同表现:
为了简洁起见,一些代码已被省略,并以表示...。
...
对于参考实现(Sun / Oracle JDK和OpenJDK),其代码appendReplacement似乎未从Java 6更改,并且当捕获组未捕获任何内容时,该代码不会追加任何内容:
} else if (nextChar == '$') { // Skip past $ cursor++; // The first number is always a group int refNum = (int)replacement.charAt(cursor) - '0'; if ((refNum < 0)||(refNum > 9)) throw new IllegalArgumentException( "Illegal group reference"); cursor++; // Capture the largest legal group string ... // Append group if (start(refNum) != -1 && end(refNum) != -1) result.append(text, start(refNum), end(refNum)); } else {
GNU类路径是Java类库的完全重新实现,appendReplacement在上述情况下具有不同的实现。在Classpath中,Classpath java.util.regex包中的类只是对中类的包装gnu.java.util.regex。
java.util.regex
gnu.java.util.regex
Matcher.appendReplacementRE.getReplacement对匹配部分进行流程替换的调用:
Matcher.appendReplacement
RE.getReplacement
public Matcher appendReplacement (StringBuffer sb, String replacement) throws IllegalStateException { assertMatchOp(); sb.append(input.subSequence(appendPosition, match.getStartIndex()).toString()); sb.append(RE.getReplacement(replacement, match, RE.REG_REPLACE_USE_BACKSLASHESCAPE)); appendPosition = match.getEndIndex(); return this; }
RE.getReplacement调用REMatch.substituteInto以获取捕获组的内容并直接附加其结果:
REMatch.substituteInto
case '$': int i1 = i + 1; while (i1 < replace.length () && Character.isDigit (replace.charAt (i1))) i1++; sb.append (m.substituteInto (replace.substring (i, i1))); i = i1 - 1; break;
REMatch.substituteIntoREMatch.toString(int)直接附加的结果,而不检查捕获组是否捕获了任何东西:
REMatch.toString(int)
if ((input.charAt (pos) == '$') && (Character.isDigit (input.charAt (pos + 1)))) { // Omitted code parses the group number into val ... if (val < start.length) { output.append (toString (val)); } }
并在捕获组未捕获时REMatch.toString(int)返回null(忽略了相关代码)。
public String toString (int sub) { if ((sub >= start.length) || sub < 0) throw new IndexOutOfBoundsException ("No group " + sub); if (start[sub] == -1) return null; ... }
因此,在GNU Classpath的情况下,null当替换字符串中指定了无法捕获任何内容的捕获组时,它将被附加到字符串中。
在Android中,Matcher.appendReplacement调用private方法appendEvaluated,该方法又将结果直接附加group(int)到替换字符串。
appendEvaluated
group(int)
public Matcher appendReplacement(StringBuffer buffer, String replacement) { buffer.append(input.substring(appendPos, start())); appendEvaluated(buffer, replacement); appendPos = end(); return this; } private void appendEvaluated(StringBuffer buffer, String s) { boolean escape = false; boolean dollar = false; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '\\' && !escape) { escape = true; } else if (c == '$' && !escape) { dollar = true; } else if (c >= '0' && c <= '9' && dollar) { buffer.append(group(c - '0')); dollar = false; } else { buffer.append(c); dollar = false; escape = false; } } // This seemingly stupid piece of code reproduces a JDK bug. if (escape) { throw new ArrayIndexOutOfBoundsException(s.length()); } }
由于Matcher.group(int)返回null用于捕获未能捕获组,Matcher.appendReplacement追加null当捕获组中的替换字符串被参考。
Matcher.group(int)
这两个抱怨您的人很可能在Android上运行他们的代码。