java - Android Bug? : String.substring(5).replace(“”, “”) // empty string -
here code:
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("", "") + "[");
and here output
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): ][ **
obviously, lines marked ** unexpected.
this issue happens android phone (lg p920 optimus 3d, android 2.3.3). while test on android phone b (lg e720 optimus chic, android 2.2), halts. guess runs infinite loop.
i have tested on both phones, java 1.5
, 1.6
. both result in same behaviour respectively.
i have tested on same eclipse java project, 1.5
, 1.6
, , 1.7
. of outputs correct, expected.
i wonder might device specific issue of implementing string.replace(“”, “”)
against string's backing array.
could please me test in devices?
could please provide me android source code of string.replace(charsequence, charsequence)
method? (like in docjar)
thanks lot!
i have modified code bit, can display on android device. (it same code anyway).
tested on both phone , phone b. behaviours still same, mentioned above.
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 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"); } }
bingo! have found bug!
thanks @izht providing link of source code. have located bug regarding problem.
this happens when string's backing array having different (longer) value actual string. in particular, when string.offset
(private variable) larger zero.
here's fix:
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 replace, return original string untouched. return this; } string replacementstring = replacement.tostring(); // empty target matches @ start , end , between each character. int targetlength = targetstring.length(); if (targetlength == 0) { int resultlength = (count + 2) * replacementstring.length(); stringbuilder result = new stringbuilder(resultlength); result.append(replacementstring); // (int = offset; < count; ++i) { // original, bug (int = offset; < (count + offset); ++i) { // fix result.append(value[i]); result.append(replacementstring); } return result.tostring(); } stringbuilder result = new stringbuilder(count); int searchstart = 0; { // copy characters before match... result.append(value, offset + searchstart, matchstart - searchstart); // insert replacement... result.append(replacementstring); // , skip on match... searchstart = matchstart + targetlength; } while ((matchstart = indexof(targetstring, searchstart)) != -1); // copy trailing chars... result.append(value, offset + searchstart, count - searchstart); return result.tostring(); }
i not sure why android has alter (and altered wrongly) replace()
in way. original java implementation doesn't have issue.
by-the-way, what's now? can it? (other using replace()
care, or throw away android phones :-/)
btw m quite sure lg e720 optimus chic (android 2.2) using different source code that one. keeps halting (suspect infinite looping) upon string.replace()
empty target string. lately found throws error message:
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): @ java.lang.abstractstringbuilder.enlargebuffer(abstractstringbuilder.java:97) 05-10 16:41:13.155: e/androidruntime(9384): @ java.lang.abstractstringbuilder.append0(abstractstringbuilder.java:157) 05-10 16:41:13.155: e/androidruntime(9384): @ java.lang.stringbuilder.append(stringbuilder.java:217) 05-10 16:41:13.155: e/androidruntime(9384): @ java.lang.string.replace(string.java:1497) 05-10 16:41:13.155: e/androidruntime(9384): @ com.example.testprojectnew.mainactivity.oncreate(mainactivity.java:22) 05-10 16:41:13.155: e/androidruntime(9384): @ android.app.instrumentation.callactivityoncreate(instrumentation.java:1047) 05-10 16:41:13.155: e/androidruntime(9384): @ android.app.activitythread.performlaunchactivity(activitythread.java:2627) 05-10 16:41:13.155: e/androidruntime(9384): @ android.app.activitythread.handlelaunchactivity(activitythread.java:2679) 05-10 16:41:13.155: e/androidruntime(9384): @ android.app.activitythread.access$2300(activitythread.java:125) 05-10 16:41:13.155: e/androidruntime(9384): @ android.app.activitythread$h.handlemessage(activitythread.java:2033) 05-10 16:41:13.155: e/androidruntime(9384): @ android.os.handler.dispatchmessage(handler.java:99) 05-10 16:41:13.155: e/androidruntime(9384): @ android.os.looper.loop(looper.java:123) 05-10 16:41:13.155: e/androidruntime(9384): @ android.app.activitythread.main(activitythread.java:4627) 05-10 16:41:13.155: e/androidruntime(9384): @ java.lang.reflect.method.invokenative(native method) 05-10 16:41:13.155: e/androidruntime(9384): @ java.lang.reflect.method.invoke(method.java:521) 05-10 16:41:13.155: e/androidruntime(9384): @ com.android.internal.os.zygoteinit$methodandargscaller.run(zygoteinit.java:878) 05-10 16:41:13.155: e/androidruntime(9384): @ com.android.internal.os.zygoteinit.main(zygoteinit.java:636) 05-10 16:41:13.155: e/androidruntime(9384): @ dalvik.system.nativestart.main(native method)
at second thought, if for-loop thingy is bug. should compile time issue. why act differently in different phones (different versions of android)?
complete workaround
got an update google, have patched it, , correct in future release.
meanwhile, have written patched method, based on their code:
(this necessary because (1) still have wait correct release, (2) need take care of devices didnt make fixed update)
/** patch string.replace(charsequence target, charsequence replacement), * because original buggy when charsequence target empty, i.e. "". * patched 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 replace, return 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(); // empty target matches @ start , end , between each character. if (targetstring.length() == 0) { // result contains original 'count' characters, copy of // replacement string before every 1 of characters, , final // copy of replacement string @ end. final stringbuilder result = new stringbuilder(count + (count + 1) * replacementstring.length()); result.append(replacementstring); (int = 0; < count; ++i) { result.append(value[i]); result.append(replacementstring); } return new string(result); // stringbuilder.tostring() not give exact length } final stringbuilder result = new stringbuilder(count); int searchstart = 0; { // copy characters before match... result.append(value, searchstart, matchstart - searchstart); // insert replacement... result.append(replacementstring); // , skip on match... searchstart = matchstart + targetstring.length(); } while ((matchstart = string.indexof(targetstring, searchstart)) != -1); // copy trailing chars... result.append(value, searchstart, count - searchstart); return new string(result); // stringbuilder.tostring() not give exact length }
the verbose version:
/** patch string.replace(charsequence target, charsequence replacement), * because original buggy when charsequence target empty, i.e. "". * patched 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 replace, return 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(); // empty target matches @ start , end , between each character. // int targetlength = targetstring.length(); // original // if (targetlength == 0) { // original if (targetstring.length() == 0) { // int resultlength = (count + 2) * replacementstring.length(); // original // // result contains original 'count' characters, copy of // // replacement string before every 1 of characters, , final // // copy of replacement string @ end. // int resultlength = count + (count + 1) * replacementstring.length(); // patched google android // stringbuilder result = new stringbuilder(resultlength); // original final stringbuilder result = new stringbuilder(count + (count + 1) * replacementstring.length()); result.append(replacementstring); // (int = offset; < count; ++i) { // original // int end = offset + count; // patched google android // (int = offset; != end; ++i) { // patched google android (int = 0; < count; ++i) { result.append(value[i]); result.append(replacementstring); } // return result.tostring(); // original return new string(result); // stringbuilder.tostring() not give exact length } // stringbuilder result = new stringbuilder(count); // original final stringbuilder result = new stringbuilder(count); int searchstart = 0; { // copy characters before match... // result.append(value, offset + searchstart, matchstart - searchstart); // original result.append(value, searchstart, matchstart - searchstart); // insert replacement... result.append(replacementstring); // , skip on match... // searchstart = matchstart + targetlength; // original searchstart = matchstart + targetstring.length(); // } while ((matchstart = indexof(targetstring, searchstart)) != -1); // original } while ((matchstart = string.indexof(targetstring, searchstart)) != -1); // copy 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() not give exact length }
Comments
Post a Comment