2

这是我的代码:

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)上进行测试时,它停止了。我猜它会陷入无限循环。

我已经在两部手机上进行了测试,使用 Java1.51.6. 两者分别导致相同的行为。

我还在同一个 Eclipse 上使用Java项目进行了测试,for 1.51.61.7. 正如预期的那样,他们的所有输出都是正确的。

我想知道这可能是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");
    }
}
4

1 回答 1

4

答对了!我找到了错误!

感谢@izht 提供源代码的链接。我已经找到了关于这个问题的错误。

这只发生在字符串的后备数组具有与实际字符串不同(更长)的值时。特别是,当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()小心使用,或者扔掉我的安卓手机:-/)


顺便说一句,我很确定我的 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 循环错误的。应该是编译时问题。为什么它在不同的手机(不同版本的 Android)中会有不同的表现?


完整的解决方法

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

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

(这是必要的,因为(1)我们仍然需要等待正确的版本,(2)我们需要照顾没有进行修复更新的设备)

/** 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
}
于 2013-05-10T08:01:03.233 回答