How can I replace two strings in a way that one does not end up replacing the other?
Let's say that I have the following code:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", word1);
story = story.replace("bar", word2);
After this code runs, the value of story
will be "Once upon a time, there was a foo and a foo."
A similar issue occurs if I replaced them in the opposite order:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("bar", word2);
story = story.replace("foo", word1);
The value of story
will be "Once upon a time, there was a bar and a bar."
My goal is to turn story
into "Once upon a time, there was a bar and a foo."
How could I accomplish that?
Solution 1:
Use the replaceEach()
method from Apache Commons StringUtils:
StringUtils.replaceEach(story, new String[]{"foo", "bar"}, new String[]{"bar", "foo"})
Solution 2:
You use an intermediate value (which is not yet present in the sentence).
story = story.replace("foo", "lala");
story = story.replace("bar", "foo");
story = story.replace("lala", "bar");
As a response to criticism: if you use a large enough uncommon string like zq515sqdqs5d5sq1dqs4d1q5dqqé"&é5d4sqjshsjddjhodfqsqc, nvùq^µù;d&€sdq: d: ;)àçàçlala and use that, it is unlikely to the point where I won't even debate it that a user will ever enter this. The only way to know whether a user will is by knowing the source code and at that point you're with a whole other level of worries.
Yes, maybe there are fancy regex ways. I prefer something readable that I know will not break out on me either.
Also reiterating the excellent advise given by @David Conrad in the comments:
Don't use some string cleverly (stupidly) chosen to be unlikely. Use characters from the Unicode Private Use Area, U+E000..U+F8FF. Remove any such characters first, since they shouldn't legitimately be in the input (they only have application-specific meaning within some application), then use them as placeholders when replacing.
Solution 3:
You can try something like this, using Matcher#appendReplacement
and Matcher#appendTail
:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
Pattern p = Pattern.compile("foo|bar");
Matcher m = p.matcher(story);
StringBuffer sb = new StringBuffer();
while (m.find()) {
/* do the swap... */
switch (m.group()) {
case "foo":
m.appendReplacement(sb, word1);
break;
case "bar":
m.appendReplacement(sb, word2);
break;
default:
/* error */
break;
}
}
m.appendTail(sb);
System.out.println(sb.toString());
Once upon a time, there was a bar and a foo.