我怎样才能以一种方式替换两个字符串,而其中一个不会最终替换另一个?

假设我有以下代码:

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);

运行此代码后,story的值将为 "Once upon a time, there was a foo and a foo."

如果我以相反的顺序替换它们,也会出现类似的问题:

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);

story的值将是 "Once upon a time, there was a bar and a bar."

我的目标是把 story变成 "Once upon a time, there was a bar and a foo."我怎样才能做到呢?

20420 次浏览

使用中间值(在句子中还没有出现)。

story = story.replace("foo", "lala");
story = story.replace("bar", "foo");
story = story.replace("lala", "bar");

作为对批评的回应: 如果您使用像 Zq515sqdqs5d5sq1dqs4d1q5dqqé”& é5d4sqjshsjddjhodfqsqc,nvùq ^ μù; d & something sdq: d: ;) àçàclala这样足够大的非常规字符串并使用它,那么用户将不太可能输入这个字符串。了解用户是否会这样做的唯一方法就是了解源代码,此时您就有了一个完全不同层次的担忧。

是的,也许正则表达式有很多花哨的方式。我更喜欢可读的东西,我知道不会打破了我。

还重申 @ David Conrad 在评论中提出的出色建议:

不要聪明地(愚蠢地)选择一些不太可能的字符串。使用 Unicode 专用区域 U + E000中的字符。.U + F 8FF.首先删除任何这样的字符,因为它们不应该合法地出现在输入中(它们只在某些应用程序中具有特定于应用程序的含义) ,然后在替换时使用它们作为占位符。

你总是可以用一个你肯定不会出现在字符串其他地方的单词替换它,然后再做第二个替换:

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", "StringYouAreSureWillNeverOccur").replace("bar", "word2").replace("StringYouAreSureWillNeverOccur", "word1");

请注意,如果 "StringYouAreSureWillNeverOccur"确实发生,这将不会正确工作。

你可以试试这样,使用 Matcher#appendReplacementMatcher#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.

这里有一个 Java8流的可能性,对某些人来说可能很有趣:

String word1 = "bar";
String word2 = "foo";


String story = "Once upon a time, there was a foo and a bar.";


// Map is from untranslated word to translated word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(word1, word2);
wordMap.put(word2, word1);


// Split on word boundaries so we retain whitespace.
String translated = Arrays.stream(story.split("\\b"))
.map(w -> wordMap.getOrDefault(w,  w))
.collect(Collectors.joining());


System.out.println(translated);

下面是 Java7中相同算法的一个近似值:

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";


// Map is from untranslated word to translated word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(word1, word2);
wordMap.put(word2, word1);


// Split on word boundaries so we retain whitespace.
StringBuilder translated = new StringBuilder();
for (String w : story.split("\\b"))
{
String tw = wordMap.get(w);
translated.append(tw != null ? tw : w);
}


System.out.println(translated);

使用 Apache Commons StringUtils中的 replaceEach()方法:

StringUtils.replaceEach(story, new String[]{"foo", "bar"}, new String[]{"bar", "foo"})

我开发的这个代码可以解决问题:

public static String change(String s,String s1, String s2) {
int length = s.length();
int x1 = s1.length();
int x2 = s2.length();
int x12 = s.indexOf(s1);
int x22 = s.indexOf(s2);
String s3=s.substring(0, x12);
String s4 =s.substring(x12+3, x22);
s=s3+s2+s4+s1;
return s;
}

主要使用 change(story,word2,word1).

搜索要替换的第一个单词。如果它在字符串中,则对出现之前的字符串部分和出现之后的字符串部分递归。

否则,请继续使用下一个要替换的单词。

幼稚的实现可能如下所示

public static String replaceAll(String input, String[] search, String[] replace) {
return replaceAll(input, search, replace, 0);
}


private static String replaceAll(String input, String[] search, String[] replace, int i) {
if (i == search.length) {
return input;
}
int j = input.indexOf(search[i]);
if (j == -1) {
return replaceAll(input, search, replace, i + 1);
}
return replaceAll(input.substring(0, j), search, replace, i + 1) +
replace[i] +
replaceAll(input.substring(j + search[i].length()), search, replace, i);
}

使用方法:

String input = "Once upon a baz, there was a foo and a bar.";
String[] search = new String[] { "foo", "bar", "baz" };
String[] replace = new String[] { "bar", "baz", "foo" };
System.out.println(replaceAll(input, search, replace));

产出:

Once upon a foo, there was a bar and a baz.

一个不那么幼稚的版本:

public static String replaceAll(String input, String[] search, String[] replace) {
StringBuilder sb = new StringBuilder();
replaceAll(sb, input, 0, input.length(), search, replace, 0);
return sb.toString();
}


private static void replaceAll(StringBuilder sb, String input, int start, int end, String[] search, String[] replace, int i) {
while (i < search.length && start < end) {
int j = indexOf(input, search[i], start, end);
if (j == -1) {
i++;
} else {
replaceAll(sb, input, start, j, search, replace, i + 1);
sb.append(replace[i]);
start = j + search[i].length();
}
}
sb.append(input, start, end);
}

不幸的是,Java 的 String没有 indexOf(String str, int fromIndex, int toIndex)方法。我在这里省略了 indexOf的实现,因为我不确定它是否正确,但是可以在 想法上找到它,以及在这里张贴的各种解决方案的一些粗略计时。

如果您希望能够处理要替换的搜索字符串的多个匹配项,那么可以通过在每个搜索字符串上分割字符串,然后替换它来轻松实现这一点。 这里有一个例子:

String regex = word1 + "|" + word2;
String[] values = Pattern.compile(regex).split(story);


String result;
foreach subStr in values
{
subStr = subStr.replace(word1, word2);
subStr = subStr.replace(word2, word1);
result += subStr;
}

只交换一个发生

如果输入中的每个可切换字符串只出现一次,则可以执行以下操作:

在进行任何替换之前,获取单词出现的索引。之后,我们只替换在这些索引中找到的单词,而不是替换所有出现的单词。该解决方案使用 StringBuilder,不产生中间 String,如 String.replace()

需要注意的一点是: 如果可交换的单词有不同的长度,在第一个替换之后,第二个索引可能会改变(如果第一个单词出现在第二个单词之前) ,正好与2个长度的差异相同。所以对齐第二个索引将确保即使我们用不同的长度交换单词也能正常工作。

public static String swap(String src, String s1, String s2) {
StringBuilder sb = new StringBuilder(src);
int i1 = src.indexOf(s1);
int i2 = src.indexOf(s2);


sb.replace(i1, i1 + s1.length(), s2); // Replace s1 with s2
// If s1 was before s2, idx2 might have changed after the replace
if (i1 < i2)
i2 += s2.length() - s1.length();
sb.replace(i2, i2 + s2.length(), s1); // Replace s2 with s1


return sb.toString();
}

交换任意出现次数

与前一种情况类似,我们将首先收集单词的索引(出现次数) ,但是在本例中,它将为每个单词提供一个整数列表,而不仅仅是一个 int。为此,我们将使用以下实用方法:

public static List<Integer> occurrences(String src, String s) {
List<Integer> list = new ArrayList<>();
for (int idx = 0;;)
if ((idx = src.indexOf(s, idx)) >= 0) {
list.add(idx);
idx += s.length();
} else
return list;
}

使用这种方法,我们可以通过减少索引(这可能需要在两个可交换的单词之间交替使用)来替换另一个单词,这样我们甚至不需要在替换之后更正索引:

public static String swapAll(String src, String s1, String s2) {
List<Integer> l1 = occurrences(src, s1), l2 = occurrences(src, s2);


StringBuilder sb = new StringBuilder(src);


// Replace occurrences by decreasing index, alternating between s1 and s2
for (int i1 = l1.size() - 1, i2 = l2.size() - 1; i1 >= 0 || i2 >= 0;) {
int idx1 = i1 < 0 ? -1 : l1.get(i1);
int idx2 = i2 < 0 ? -1 : l2.get(i2);
if (idx1 > idx2) { // Replace s1 with s2
sb.replace(idx1, idx1 + s1.length(), s2);
i1--;
} else { // Replace s2 with s1
sb.replace(idx2, idx2 + s2.length(), s1);
i2--;
}
}


return sb.toString();
}

这不是一个简单的问题。搜索替换参数越多,就越麻烦。你有几种选择,分散在丑陋-优雅,高效-浪费的调色板上:

  • 按照 @ AlanHay的建议使用 Apache Commons 中的 StringUtils.replaceEach。如果您可以在项目中自由地添加新的依赖项,那么这是一个很好的选择。您可能很幸运: 依赖项可能已经包含在项目中

  • 按照 @ Jeroen的建议使用一个临时占位符,并分两个步骤执行替换:

    1. 用原始文本中不存在的唯一标记替换所有搜索模式
    2. 用真正的目标替换替换占位符

    这不是一个很好的方法,原因有几个: 它需要确保第一步中使用的标记是真正唯一的; 它执行的字符串替换操作比实际需要的更多

  • 根据所有的模式构建一个正则表达式,并使用 @ arshajii建议的 ABC0和 StringBuffer方法。这并不可怕,但也不是那么伟大,因为构建正则表达式是一种骇客行为,而且它涉及到 StringBufferStringBuffer在一段时间以前已经过时,它支持 StringBuilder

  • 使用 @ mjolka提出的递归解决方案,在匹配的模式处分割字符串,并在剩余的段上递归。这是一个很好的解决方案,紧凑,相当优雅。它的弱点是潜在的许多子字符串和连接操作,以及适用于所有递归解决方案的堆栈大小限制

  • 将文本分割为单词,并使用 Java 8流来优雅地执行替身计画,就像 @ msandiford建议的那样,但当然,这只有在你同意在单词边界上进行分割的情况下才能实现,这使得它不适合作为一个通用的解决方案

这是我的版本,基于从 Apache 的实现借用的想法。它既不简单也不优雅,但是它可以工作,而且应该相对高效,没有不必要的步骤。简而言之,它的工作原理是这样的: 在文本中反复寻找下一个匹配的搜索模式,然后使用一个 StringBuilder来积累未匹配的片段和替身计画。

public static String replaceEach(String text, String[] searchList, String[] replacementList) {
// TODO: throw new IllegalArgumentException() if any param doesn't make sense
//validateParams(text, searchList, replacementList);


SearchTracker tracker = new SearchTracker(text, searchList, replacementList);
if (!tracker.hasNextMatch(0)) {
return text;
}


StringBuilder buf = new StringBuilder(text.length() * 2);
int start = 0;


do {
SearchTracker.MatchInfo matchInfo = tracker.matchInfo;
int textIndex = matchInfo.textIndex;
String pattern = matchInfo.pattern;
String replacement = matchInfo.replacement;


buf.append(text.substring(start, textIndex));
buf.append(replacement);


start = textIndex + pattern.length();
} while (tracker.hasNextMatch(start));


return buf.append(text.substring(start)).toString();
}


private static class SearchTracker {


private final String text;


private final Map<String, String> patternToReplacement = new HashMap<>();
private final Set<String> pendingPatterns = new HashSet<>();


private MatchInfo matchInfo = null;


private static class MatchInfo {
private final String pattern;
private final String replacement;
private final int textIndex;


private MatchInfo(String pattern, String replacement, int textIndex) {
this.pattern = pattern;
this.replacement = replacement;
this.textIndex = textIndex;
}
}


private SearchTracker(String text, String[] searchList, String[] replacementList) {
this.text = text;
for (int i = 0; i < searchList.length; ++i) {
String pattern = searchList[i];
patternToReplacement.put(pattern, replacementList[i]);
pendingPatterns.add(pattern);
}
}


boolean hasNextMatch(int start) {
int textIndex = -1;
String nextPattern = null;


for (String pattern : new ArrayList<>(pendingPatterns)) {
int matchIndex = text.indexOf(pattern, start);
if (matchIndex == -1) {
pendingPatterns.remove(pattern);
} else {
if (textIndex == -1 || matchIndex < textIndex) {
textIndex = matchIndex;
nextPattern = pattern;
}
}
}


if (nextPattern != null) {
matchInfo = new MatchInfo(nextPattern, patternToReplacement.get(nextPattern), textIndex);
return true;
}
return false;
}
}

单元测试:

@Test
public void testSingleExact() {
assertEquals("bar", StringUtils.replaceEach("foo", new String[]{"foo"}, new String[]{"bar"}));
}


@Test
public void testReplaceTwice() {
assertEquals("barbar", StringUtils.replaceEach("foofoo", new String[]{"foo"}, new String[]{"bar"}));
}


@Test
public void testReplaceTwoPatterns() {
assertEquals("barbaz", StringUtils.replaceEach("foobar",
new String[]{"foo", "bar"},
new String[]{"bar", "baz"}));
}


@Test
public void testReplaceNone() {
assertEquals("foofoo", StringUtils.replaceEach("foofoo", new String[]{"x"}, new String[]{"bar"}));
}


@Test
public void testStory() {
assertEquals("Once upon a foo, there was a bar and a baz, and another bar and a cat.",
StringUtils.replaceEach("Once upon a baz, there was a foo and a bar, and another foo and a cat.",
new String[]{"foo", "bar", "baz"},
new String[]{"bar", "baz", "foo"})
);
}

Java8中的一行程序:

    story = Pattern
.compile(String.format("(?<=%1$s)|(?=%1$s)", "foo|bar"))
.splitAsStream(story)
.map(w -> ImmutableMap.of("bar", "foo", "foo", "bar").getOrDefault(w, w))
.collect(Collectors.joining());

如果您想要替换一个句子中由空格分隔的单词,如您的示例所示,您可以使用这个简单的算法。

  1. 空白处的分裂故事
  2. 如果 foo 将每个元素替换为 bar,则替换为 Vice varsa
  3. 将数组连接回一个字符串

如果在空间上分裂是不可接受的,可以遵循这个替代算法。您需要首先使用较长的字符串。如果琴弦是愚弄和愚弄,你需要先用愚弄,然后再用愚弄。

  1. 在单词 foo 上分裂
  2. 用 foo 替换数组中的每个元素
  3. 加入该数组,除了最后一个元素外,在每个元素后面添加 bar

使用 String.regionMatches编写这样一个方法很容易:

public static String simultaneousReplace(String subject, String... pairs) {
if (pairs.length % 2 != 0) throw new IllegalArgumentException(
"Strings to find and replace are not paired.");
StringBuilder sb = new StringBuilder();
outer:
for (int i = 0; i < subject.length(); i++) {
for (int j = 0; j < pairs.length; j += 2) {
String find = pairs[j];
if (subject.regionMatches(i, find, 0, find.length())) {
sb.append(pairs[j + 1]);
i += find.length() - 1;
continue outer;
}
}
sb.append(subject.charAt(i));
}
return sb.toString();
}

测试:

String s = "There are three cats and two dogs.";
s = simultaneousReplace(s,
"cats", "dogs",
"dogs", "budgies");
System.out.println(s);

产出:

有三只狗和两只虎皮鹦鹉。

虽然不是很明显,但是像这样的函数仍然可以依赖于指定替身计画的顺序。考虑一下:

String truth = "Java is to JavaScript";
truth += " as " + simultaneousReplace(truth,
"JavaScript", "Hamster",
"Java", "Ham");
System.out.println(truth);

产出:

Java 对于 JavaScript 就像 Ham 对于 Hamster

但反过来的替身计画是:

truth += " as " + simultaneousReplace(truth,
"Java", "Ham",
"JavaScript", "Hamster");

产出:

Java 之于 JavaScript 就像 Ham 之于 HamScript

哎呀! :)

因此,有时确保寻找 最长的匹配是有用的(例如,PHP 的 strtr函数就是这样做的)。这个版本的方法可以做到这一点:

public static String simultaneousReplace(String subject, String... pairs) {
if (pairs.length % 2 != 0) throw new IllegalArgumentException(
"Strings to find and replace are not paired.");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < subject.length(); i++) {
int longestMatchIndex = -1;
int longestMatchLength = -1;
for (int j = 0; j < pairs.length; j += 2) {
String find = pairs[j];
if (subject.regionMatches(i, find, 0, find.length())) {
if (find.length() > longestMatchLength) {
longestMatchIndex = j;
longestMatchLength = find.length();
}
}
}
if (longestMatchIndex >= 0) {
sb.append(pairs[longestMatchIndex + 1]);
i += longestMatchLength - 1;
} else {
sb.append(subject.charAt(i));
}
}
return sb.toString();
}

请注意,上述方法是区分大小写的。如果您需要不区分大小写的版本,那么很容易修改以上内容,因为 String.regionMatches可以采用 ignoreCase参数。

下面是使用 Map 的一个不那么复杂的答案。

private static String replaceEach(String str,Map<String, String> map) {


Object[] keys = map.keySet().toArray();
for(int x = 0 ; x < keys.length ; x ++ ) {
str = str.replace((String) keys[x],"%"+x);
}


for(int x = 0 ; x < keys.length ; x ++) {
str = str.replace("%"+x,map.get(keys[x]));
}
return str;
}

方法被调用

Map<String, String> replaceStr = new HashMap<>();
replaceStr.put("Raffy","awesome");
replaceStr.put("awesome","Raffy");
String replaced = replaceEach("Raffy is awesome, awesome awesome is Raffy Raffy", replaceStr);

产出为: Raffy 太棒了 Raffy Raffy 太棒了

使用找到的 给你答案,您可以找到所有要替换的字符串的匹配项。

例如,在上面的 SO 答案中运行代码。创建两个索引表(假设 bar 和 foo 不会在字符串中只出现一次) ,您可以使用这些表在字符串中替换它们。

现在可以用来替换特定索引位置:

public static String replaceStringAt(String s, int pos, String c) {
return s.substring(0,pos) + c + s.substring(pos+1);
}

pos是字符串开始的索引(从上面引用的索引表开始)。 假设您为每个索引创建了两个索引表。 我们称之为 indexBarindexFoo

现在,在替换它们时,您可以简单地运行两个循环,每个循环对应于您希望进行的替换。

for(int i=0;i<indexBar.Count();i++)
replaceStringAt(originalString,indexBar[i],newString);

类似地,indexFoo的另一个循环。

这可能没有其他答案那么有效,但它比地图或其他东西更容易理解。

这将始终给出您希望的结果,并且每个字符串可能出现多次。只要存储每个事件的索引。

此外,这个答案不需要递归,也不需要任何外部依赖关系。就复杂性而言,它可能是 O (n 的平方) ,而 n 是两个单词出现次数的总和。

如果您不想要任何依赖项,您可以简单地使用一个只允许一次性更改的数组。这不是最有效的解决方案,但它应该是有效的。

public String replace(String sentence, String[]... replace){
String[] words = sentence.split("\\s+");
int[] lock = new int[words.length];
StringBuilder out = new StringBuilder();


for (int i = 0; i < words.length; i++) {
for(String[] r : replace){
if(words[i].contains(r[0]) && lock[i] == 0){
words[i] = words[i].replace(r[0], r[1]);
lock[i] = 1;
}
}


out.append((i < (words.length - 1) ? words[i] + " " : words[i]));
}


return out.toString();
}

那么,应该可以。

String story = "Once upon a time, there was a foo and a bar.";


String[] a = {"foo", "bar"};
String[] b = {"bar", "foo"};
String[] c = {"there", "Pocahontas"};
story = replace(story, a, b, c);


System.out.println(story); // Once upon a time, Pocahontas was a bar and a foo.

考虑使用 StringBuilder

然后将索引存储在每个字符串应该开始的位置。如果在每个位置使用占位符,则删除它,并插入用户字符串。然后可以通过将字符串长度添加到起始位置来映射结束位置。

String firstString = "???";
String secondString  = "???"


StringBuilder story = new StringBuilder("One upon a time, there was a "
+ firstString
+ " and a "
+ secondString);


int  firstWord = 30;
int  secondWord = firstWord + firstString.length() + 7;


story.replace(firstWord, firstWord + firstString.length(), userStringOne);
story.replace(secondWord, secondWord + secondString.length(), userStringTwo);


firstString = userStringOne;
secondString = userStringTwo;


return story;

我只能分享我自己的方法。

您可以使用临时 String temp = "<?>";String.Format();

这是我的示例代码,通过控制台应用 - “只有想法,不是准确的答案”创建。

static void Main(string[] args)
{
String[] word1 = {"foo", "Once"};
String[] word2 = {"bar", "time"};
String story = "Once upon a time, there was a foo and a bar.";


story = Switcher(story,word1,word2);
Console.WriteLine(story);
Console.Read();
}
// Using a temporary string.
static string Switcher(string text, string[] target, string[] value)
{
string temp = "<?>";
if (target.Length == value.Length)
{
for (int i = 0; i < target.Length; i++)
{
text = text.Replace(target[i], temp);
text = text.Replace(value[i], target[i]);
text = text.Replace(temp, value[i]);
}
}
return text;
}

或者你也可以使用 String.Format();

static string Switcher(string text, string[] target, string[] value)
{
if (target.Length == value.Length)
{
for (int i = 0; i < target.Length; i++)
{
text = text.Replace(target[i], "{0}").Replace(value[i], "{1}");
text = String.Format(text, value[i], target[i]);
}
}
return text;
}

输出: time upon a Once, there was a bar and a foo.

您可以通过以下代码块实现您的目标:

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = String.format(story.replace(word1, "%1$s").replace(word2, "%2$s"),
word2, word1);

不管顺序如何,它都会替换单词。你可以把这个原则扩展成一个实用的方法,比如:

private static String replace(String source, String[] targets, String[] replacements) throws IllegalArgumentException {
if (source == null) {
throw new IllegalArgumentException("The parameter \"source\" cannot be null.");
}


if (targets == null || replacements == null) {
throw new IllegalArgumentException("Neither parameters \"targets\" or \"replacements\" can be null.");
}


if (targets.length == 0 || targets.length != replacements.length) {
throw new IllegalArgumentException("The parameters \"targets\" and \"replacements\" must have at least one item and have the same length.");
}


String outputMask = source;
for (int i = 0; i < targets.length; i++) {
outputMask = outputMask.replace(targets[i], "%" + (i + 1) + "$s");
}


return String.format(outputMask, (Object[])replacements);
}

它们将被消耗为:

String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = replace(story, new String[] { "bar", "foo" },
new String[] { "foo", "bar" }));
String word1 = "bar";
String word2 = "foo";


String story = "Once upon a time, there was a foo and a bar."


story = story.replace("foo", "<foo />");
story = story.replace("bar", "<bar />");


story = story.replace("<foo />", word1);
story = story.replace("<bar />", word2);

您正在对输入执行多个搜索替换操作。当替换字符串包含搜索字符串时,这将产生不希望的结果。考虑 foo-> bar,bar-foo 的例子,下面是每次迭代的结果:

  1. 从前,有一个 foo 和一个 bar
  2. 从前,有一个酒吧和一个酒吧。(foo-> bar)
  3. 从前,有一个 foo 和一个 foo (bar-> foo,output)

您需要在一个迭代中执行替换,而不需要返回:

  1. 从当前位置到结束搜索多个搜索字符串,直到找到匹配
  2. 用相应的替换字符串替换匹配的搜索字符串
  3. 将当前位置设置为替换字符串后的下一个字符
  4. 重复

String.indexOfAny(String[]) -> int[]{index, whichString}这样的函数会很有用,下面是一个例子(不是最有效的一个) :

private static String replaceEach(String str, String[] searchWords, String[] replaceWords) {
String ret = "";
while (str.length() > 0) {
int i;
for (i = 0; i < searchWords.length; i++) {
String search = searchWords[i];
String replace = replaceWords[i];
if (str.startsWith(search)) {
ret += replace;
str = str.substring(search.length());
break;
}
}
if (i == searchWords.length) {
ret += str.substring(0, 1);
str = str.substring(1);
}
}
return ret;
}

一些测试:

System.out.println(replaceEach(
"Once upon a time, there was a foo and a bar.",
new String[]{"foo", "bar"},
new String[]{"bar", "foo"}
));
// Once upon a time, there was a bar and a foo.


System.out.println(replaceEach(
"a p",
new String[]{"a", "p"},
new String[]{"apple", "pear"}
));
// apple pear


System.out.println(replaceEach(
"ABCDE",
new String[]{"A", "B", "C", "D", "E"},
new String[]{"B", "C", "E", "E", "F"}
));
// BCEEF


System.out.println(replaceEach(
"ABCDEF",
new String[]{"ABCDEF", "ABC", "DEF"},
new String[]{"XXXXXX", "YYY", "ZZZ"}
));
// XXXXXX
// note the order of search strings, longer strings should be placed first
// in order to make the replacement greedy

IDEONE 演示
IDEONE 上的演示,备用代码

这是我的版本,基于文字的:

class TextReplace
{


public static void replaceAll (String text, String [] lookup,
String [] replacement, String delimiter)
{


String [] words = text.split(delimiter);


for (int i = 0; i < words.length; i++)
{


int j = find(lookup, words[i]);


if (j >= 0) words[i] = replacement[j];


}


text = StringUtils.join(words, delimiter);


}


public static  int find (String [] array, String key)
{


for (int i = 0; i < array.length; i++)
if (array[i].equals(key))
return i;


return (-1);


}


}
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."

有点棘手,但你需要做更多的检查。

1. 将字符串转换为字符数组

   String temp[] = story.split(" ");//assume there is only spaces.

2.用 bar代替 foo,用 foo代替 bar,因为没有机会再次得到可替换的字符串。

这很有效,也很简单:

public String replaceBoth(String text, String token1, String token2) {
return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
}

你可以这样使用:

replaceBoth("Once upon a time, there was a foo and a bar.", "foo", "bar");

注意: 这取决于不包含字符 \ufdd0的字符串,这是 Unicode 内部使用的字符 永久性的(参见 http://www.unicode.org/faq/private_use.html) :

我不认为这是必要的,但如果你想绝对安全,你可以使用:

public String replaceBoth(String text, String token1, String token2) {
if (text.contains("\ufdd0") || token1.contains("\ufdd0") || token2.contains("\ufdd0")) throw new IllegalArgumentException("Invalid character.");
return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
}

简短的回答是。

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
story = story.replace("foo", "@"+ word1).replace("bar", word2).replace("@" + word2, word1);
System.out.println(story);