宾果!我发现了这个错误!
谢谢@izht提供的链接source code。我找到了关于这个问题的错误。
只有当字符串的backing array与实际字符串具有不同(较长)的值时才会发生这种情况。特别是,当String.offset
(私有变量)大于零时。
这里的修复:
public String replace(CharSequence target, CharSequence replacement) {
if (target == null) {
throw new NullPointerException("target == null");
}
if (replacement == null) {
throw new NullPointerException("replacement == null");
}
String targetString = target.toString();
int matchStart = indexOf(targetString, 0);
if (matchStart == -1) {
// If there's nothing to replace, return the original string untouched.
return this;
}
String replacementString = replacement.toString();
// The empty target matches at the start and end and between each character.
int targetLength = targetString.length();
if (targetLength == 0) {
int resultLength = (count + 2) * replacementString.length();
StringBuilder result = new StringBuilder(resultLength);
result.append(replacementString);
// for (int i = offset; i < count; ++i) { // original, bug
for (int i = offset; i < (count + offset); ++i) { // fix
result.append(value[i]);
result.append(replacementString);
}
return result.toString();
}
StringBuilder result = new StringBuilder(count);
int searchStart = 0;
do {
// Copy characters before the match...
result.append(value, offset + searchStart, matchStart - searchStart);
// Insert the replacement...
result.append(replacementString);
// And skip over the match...
searchStart = matchStart + targetLength;
} while ((matchStart = indexOf(targetString, searchStart)) != -1);
// Copy any trailing chars...
result.append(value, offset + searchStart, count - searchStart);
return result.toString();
}
我不知道为什么Android已经改变(和错误地改变)以这种方式replace()
。原始的Java实现没有这个问题。
顺便说一下,现在在做什么?我能用它做什么? (不是使用replace()
格外小心,或者扔掉我的Android手机其他: - /)
顺便说一句读音字十分肯定我的LG E720擎天柱别致(安卓2。2)使用与that one不同的源代码。它在String.replace()
和空的目标字符串上一直停止(怀疑无限循环)。最近我发现它抛出这个错误消息:
05-10 16:41:13.155: E/AndroidRuntime(9384): FATAL EXCEPTION: main
05-10 16:41:13.155: E/AndroidRuntime(9384): java.lang.OutOfMemoryError
05-10 16:41:13.155: E/AndroidRuntime(9384): at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:97)
05-10 16:41:13.155: E/AndroidRuntime(9384): at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:157)
05-10 16:41:13.155: E/AndroidRuntime(9384): at java.lang.StringBuilder.append(StringBuilder.java:217)
05-10 16:41:13.155: E/AndroidRuntime(9384): at java.lang.String.replace(String.java:1497)
05-10 16:41:13.155: E/AndroidRuntime(9384): at com.example.testprojectnew.MainActivity.onCreate(MainActivity.java:22)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.app.ActivityThread.access$2300(ActivityThread.java:125)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.os.Handler.dispatchMessage(Handler.java:99)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.os.Looper.loop(Looper.java:123)
05-10 16:41:13.155: E/AndroidRuntime(9384): at android.app.ActivityThread.main(ActivityThread.java:4627)
05-10 16:41:13.155: E/AndroidRuntime(9384): at java.lang.reflect.Method.invokeNative(Native Method)
05-10 16:41:13.155: E/AndroidRuntime(9384): at java.lang.reflect.Method.invoke(Method.java:521)
05-10 16:41:13.155: E/AndroidRuntime(9384): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)
05-10 16:41:13.155: E/AndroidRuntime(9384): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:636)
05-10 16:41:13.155: E/AndroidRuntime(9384): at dalvik.system.NativeStart.main(Native Method)
在第二个想法,如果for循环啄是的bug。这应该是一个编译时问题。为什么它会在不同的手机(不同版本的Android)中采取不同的行为?
完整的解决方法
了an update来自谷歌,他们已经修复了它,并将纠正in the future release。
同时,我写了一个补丁的方法,基于their code:
(这是必要的,因为(1)我们仍然必须等待正确的版本,(2),我们需要照顾的设备这didnt作出这样的固定更新)
/** Patch for the String.replace(CharSequence target, CharSequence replacement),
* because the original is buggy when CharSequence target is empty, i.e. "".
* Patched by Google Android: https://android-review.googlesource.com/58393
*/
public static String replacePatched(final String string, final CharSequence target, final CharSequence replacement) {
if (target == null) {
throw new NullPointerException("target == null");
}
if (replacement == null) {
throw new NullPointerException("replacement == null");
}
final String targetString = target.toString();
int matchStart = string.indexOf(targetString, 0);
if (matchStart == -1) {
// If there's nothing to replace, return the original string untouched.
return new String(string);
}
final char[] value = string.toCharArray(); // required in patch
final int count = value.length; // required in patch
final String replacementString = replacement.toString();
// The empty target matches at the start and end and between each character.
if (targetString.length() == 0) {
// The result contains the original 'count' characters, a copy of the
// replacement string before every one of those characters, and a final
// copy of the replacement string at the end.
final StringBuilder result = new StringBuilder(count + (count + 1) * replacementString.length());
result.append(replacementString);
for (int i = 0; i < count; ++i) {
result.append(value[i]);
result.append(replacementString);
}
return new String(result); // StringBuilder.toString() does not give exact length
}
final StringBuilder result = new StringBuilder(count);
int searchStart = 0;
do {
// Copy characters before the match...
result.append(value, searchStart, matchStart - searchStart);
// Insert the replacement...
result.append(replacementString);
// And skip over the match...
searchStart = matchStart + targetString.length();
} while ((matchStart = string.indexOf(targetString, searchStart)) != -1);
// Copy any trailing chars...
result.append(value, searchStart, count - searchStart);
return new String(result); // StringBuilder.toString() does not give exact length
}
的详细的版本:
/** Patch for the String.replace(CharSequence target, CharSequence replacement),
* because the original is buggy when CharSequence target is empty, i.e. "".
* Patched by Google Android: https://android-review.googlesource.com/58393
*/
public static String replacePatched(final String string, final CharSequence target, final CharSequence replacement) {
if (target == null) {
throw new NullPointerException("target == null");
}
if (replacement == null) {
throw new NullPointerException("replacement == null");
}
// String targetString = target.toString(); // original
final String targetString = target.toString();
// int matchStart = indexOf(targetString, 0); // original
int matchStart = string.indexOf(targetString, 0);
if (matchStart == -1) {
// If there's nothing to replace, return the original string untouched.
// return this; // original
return new String(string);
}
final char[] value = string.toCharArray(); // required in patch
final int count = value.length; // required in patch
// String replacementString = replacement.toString(); // original
final String replacementString = replacement.toString();
// The empty target matches at the start and end and between each character.
// int targetLength = targetString.length(); // original
// if (targetLength == 0) { // original
if (targetString.length() == 0) {
// int resultLength = (count + 2) * replacementString.length(); // original
// // The result contains the original 'count' characters, a copy of the
// // replacement string before every one of those characters, and a final
// // copy of the replacement string at the end.
// int resultLength = count + (count + 1) * replacementString.length(); // patched by Google Android
// StringBuilder result = new StringBuilder(resultLength); // original
final StringBuilder result = new StringBuilder(count + (count + 1) * replacementString.length());
result.append(replacementString);
// for (int i = offset; i < count; ++i) { // original
// int end = offset + count; // patched by Google Android
// for (int i = offset; i != end; ++i) { // patched by Google Android
for (int i = 0; i < count; ++i) {
result.append(value[i]);
result.append(replacementString);
}
// return result.toString(); // original
return new String(result); // StringBuilder.toString() does not give exact length
}
// StringBuilder result = new StringBuilder(count); // original
final StringBuilder result = new StringBuilder(count);
int searchStart = 0;
do {
// Copy characters before the match...
// result.append(value, offset + searchStart, matchStart - searchStart); // original
result.append(value, searchStart, matchStart - searchStart);
// Insert the replacement...
result.append(replacementString);
// And skip over the match...
// searchStart = matchStart + targetLength; // original
searchStart = matchStart + targetString.length();
// } while ((matchStart = indexOf(targetString, searchStart)) != -1); // original
} while ((matchStart = string.indexOf(targetString, searchStart)) != -1);
// Copy any trailing chars...
// result.append(value, offset + searchStart, count - searchStart); // original
result.append(value, searchStart, count - searchStart);
// return result.toString(); // original
return new String(result); // StringBuilder.toString() does not give exact length
}
如果你需要做大量的STR在操作上,有一个称为StringBuilder的类更适合于这个目的。 (StringBuffer如果字符串需要由不同的线程操纵,StringBuilder如果不是) – gparyani 2013-05-10 05:49:22
嗯......感谢您的建议。但是这只是一个'substring()',后面跟着一个'replace()'。我想,创建'StringBuilder'并不是那么重要。 – midnite 2013-05-10 05:52:10