2013-05-10 81 views
2

这里是我的代码: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.51.61.7。如预期的那样,它们的所有输出都是正确的。

我不知道这可能是实现String.replace(“”, “”)对字符串的backing array的设备特定问题。

你能帮我试一下你的设备吗?

请问谁能给我提供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"); 
    } 
} 
+0

如果你需要做大量的STR在操作上,有一个称为StringBuilder的类更适合于这个目的。 (StringBuffer如果字符串需要由不同的线程操纵,StringBuilder如果不是) – gparyani 2013-05-10 05:49:22

+1

嗯......感谢您的建议。但是这只是一个'substring()',后面跟着一个'replace()'。我想,创建'StringBuilder'并不是那么重要。 – midnite 2013-05-10 05:52:10

回答

4

宾果!我发现了这个错误!

谢谢@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 
} 
+0

提交错误:https://code.google.com/p/android/issues/detail?id=55129 – midnite 2013-05-10 11:32:40

+0

刚刚在我姐姐的三星Galaxy S2(Android 4.1.2)上测试过,同样的错误发生,相同行为作为我的LG Optimus 3D(Android 2.3.3)。 – midnite 2013-05-11 07:28:11