这是我的代码:
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.5
和1.6
。两者分别导致相同的行为。
我还在我的同一个Eclipse上使用 Java 项目测试了1.5
,1.6
和1.7
。正如预期的那样,它们的所有输出都是正确的。
我想知道这可能是针对字符串backing array实施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");
}
}
答案 0 :(得分:4)
感谢@izht提供source code的链接。我找到了有关此问题的错误。
仅当String的backing array具有与实际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)使用的源代码与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循环的东西是那个错误。它应该是一个编译时问题。为什么它会在不同的手机(不同版本的Android)中采取不同的行为?
来自Google的an update,他们已对其进行了修补,并将其更正in the future release。
与此同时,我根据their code编写了一个修补方法:
(这是必要的,因为(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
}