Android Bug? :String.substring(5).replace(“”,“”)//空字符串

这是我的代码:

String str = "just_a_string"; System.out.println("]" + str + "["); System.out.println("]" + str.replace("", "") + "["); System.out.println("]" + str.substring(5) + "["); System.out.println("]" + str.substring(5).replace("", "") + "["); System.out.println("]" + str.substring(3, 8) + "["); System.out.println("]" + str.substring(3, 8).replace("", "") + "["); System.out.println("]" + "sdajndan".substring(5).replace("", "") + "["); 

这是输出

 05-09 19:09:20.570: I/System.out(23801): ]just_a_string[ 05-09 19:09:20.570: I/System.out(23801): ]just_a_string[ 05-09 19:09:20.570: I/System.out(23801): ]a_string[ 05-09 19:09:20.570: I/System.out(23801): ]a_s[ ** 05-09 19:09:20.570: I/System.out(23801): ]t_a_s[ 05-09 19:09:20.570: I/System.out(23801): ]t_[ ** 05-09 19:09:20.570: I/System.out(23801): ][ ** 

显然,标有**的线条是出乎意料的。

这个问题发生在我的Android手机A(LG P920 Optimus 3D,Android 2.3.3)上。 当我在我的Android手机B(LG E720 Optimus Chic,Android 2.2)上测试时,它停止了。 我猜它会遇到无限循环。

我已经在两款手机上测试过Java 1.51.6 。 两者分别导致相同的行为。

我还使用Java项目在我的同一个Eclipse上测试了1.61.7 。 正如所料,它们的所有输出都是正确的。

我想知道这可能是针对String的后备数组实现String.replace(“”, “”)的设备特定问题。

你能帮我测试你的设备吗?

有谁能请给我提供String.replace(CharSequence, CharSequence)方法的Android源代码? (就像在docjar中 )

非常感谢!


我已经修改了一下代码,所以它也可以在Android设备上显示。 (无论如何它都是相同的代码)。

在我的手机A和手机B上进行了测试。如上所述,行为仍然相同。

 package com.example.testprojectnew; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity { String output_text = ""; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String str = "just_a_string"; process("1]" + str + "["); process("2]" + str.replace("", "") + "["); process("3]" + str.substring(5) + "["); process("4]" + str.substring(5).replace("", "") + "["); process("5]" + str.substring(3, 8) + "["); process("6]" + str.substring(3, 8).replace("", "") + "["); process("7]" + "sdajndan".substring(5).replace("", "") + "["); output_text = output_text.concat("\n\nLines (1 & 2), (3 & 4), (5 & 6), should be the same."); ((TextView) findViewById(R.id.a_string)).setText(output_text); } private void process(String str) { System.out.println(str); output_text = output_text.concat(str).concat("\n"); } } 

答对了! 我发现了这个错误!

感谢@izht提供源代码的链接。 我找到了有关此问题的错误。

仅当String的后备数组具有与实际String不同(更长)的值时才会发生这种情况。 特别是,当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 Optimus Chic(Android 2.2)使用的是不同的源代码。 它在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-loop thingy bug。 它应该是编译时问题。 为什么它会在不同的手机(不同版本的Android)中采取不同的行为?


完整的解决方法

获得了谷歌的更新 ,他们已经修补了它,并将在未来版本中更正它。

同时,我根据他们的代码编写了一个修补方法:

(这是必要的,因为(1)我们仍然需要等待正确的发布,(2)我们需要处理没有进行固定更新的设备)

 /** Patch for the String.replace(CharSequence target, CharSequence replacement), * because the original is buggy when CharSequence target is empty, ie "". * 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, ie "". * 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 }