当替换文本与搜索文本重叠时,用Java替换多个子字符串

假设您有以下字符串:

cat dog fish dog fish cat 

你想用dogs取代所有的cats ,用fish取代所有的dogs ,用cats取代所有的fish 。 直观地说,预期结果:

 dog fish cat fish cat dog 

如果你尝试使用replaceAll()循环的明显解决方案,你得到:

  1. (原创) cat dog fish dog fish cat
  2. (猫 – >狗) dog dog fish dog fish dog
  3. (狗 – >鱼) fish fish fish fish fish fish
  4. (鱼 – >猫) cat cat cat cat cat cat

显然,这不是预期的结果。 那么最简单的方法是什么? 我可以和PatternMatcher (以及很多Pattern.quote()Matcher.quoteReplacement() )一起拼凑一些东西,但我拒绝相信我是第一个Matcher.quoteReplacement()这个问题的人,并且没有库函数来解决它。

(FWIW,实际案例有点复杂,不涉及直接交换。)

似乎在apache commons中的StringUtils.replaceEach做你想要的:

 StringUtils.replaceEach("abcdeab", new String[]{"ab", "cd"}, new String[]{"cd", "ab"}); // returns "cdabecd" 

请注意,上述链接中的文档似乎有误。 请参阅以下评论了解详情。

 String rep = str.replace("cat","§1§").replace("dog","§2§") .replace("fish","§3§").replace("§1§","dog") .replace("§2§","fish").replace("§3§","cat"); 

像地狱一样丑陋和低效,但有效。


好的,这是一个更复杂和通用的版本。 我更喜欢使用正则表达式而不是扫描仪。 这样我就可以替换任意字符串,而不仅仅是单词(可以更好或更差)。 无论如何,这里是:

 public static String replace( final String input, final Map replacements) { if (input == null || "".equals(input) || replacements == null || replacements.isEmpty()) { return input; } StringBuilder regexBuilder = new StringBuilder(); Iterator it = replacements.keySet().iterator(); regexBuilder.append(Pattern.quote(it.next())); while (it.hasNext()) { regexBuilder.append('|').append(Pattern.quote(it.next())); } Matcher matcher = Pattern.compile(regexBuilder.toString()).matcher(input); StringBuffer out = new StringBuffer(input.length() + (input.length() / 10)); while (matcher.find()) { matcher.appendReplacement(out, replacements.get(matcher.group())); } matcher.appendTail(out); return out.toString(); } 

测试代码:

 System.out.println(replace("cat dog fish dog fish cat", ImmutableMap.of("cat", "dog", "dog", "fish", "fish", "cat"))); 

输出:

狗鱼猫鱼猫狗

显然这个解决方案只对许多替代品有意义,否则这是一个巨大的矫枉过正。

我会创建一个StringBuilder然后解析文本一次一次一个字,转移不变的单词或改变单词。 我建议您不会为每次交换解析它。

所以不要做类似的事情:

 // pseudocode text is new text swapping cat with dog text is new text swapping dog with fish text is new text swapping fish with cat 

我会做

 for each word in text if word is cat, swap with dog if word is dog, swap with fish if word is fish, swap with cat transfer new word (or unchanged word) into StringBuilder. 

我可能会为此创建一个swap(…)方法并使用HashMap进行交换。

例如

 import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class SwapWords { private static Map myMap = new HashMap(); public static void main(String[] args) { // this would really be loaded using a file such as a text file or xml // or even a database: myMap.put("cat", "dog"); myMap.put("dog", "fish"); myMap.put("fish", "dog"); String testString = "cat dog fish dog fish cat"; StringBuilder sb = new StringBuilder(); Scanner testScanner = new Scanner(testString); while (testScanner.hasNext()) { String text = testScanner.next(); text = myMap.get(text) == null ? text : myMap.get(text); sb.append(text + " "); } System.out.println(sb.toString().trim()); } } 
 public class myreplase { public Map replase; public myreplase() { replase = new HashMap(); replase.put("a", "Apple"); replase.put("b", "Banana"); replase.put("c", "Cantalope"); replase.put("d", "Date"); String word = "abcdabcd"; String ss = ""; Iterator i = replase.keySet().iterator(); while (i.hasNext()) { ss += i.next(); if (i.hasNext()) { ss += "|"; } } Pattern pattern = Pattern.compile(ss); StringBuilder buffer = new StringBuilder(); for (int j = 0, k = 1; j < word.length(); j++,k++) { String s = word.substring(j, k); Matcher matcher = pattern.matcher(s); if (matcher.find()) { buffer.append(replase.get(s)); } else { buffer.append(s); } } System.out.println(buffer.toString()); } public static void main(String[] args) { new myreplase(); } } 

输出: - Apple Banana Cantalope Date Apple Banana Cantalope Date

这是一种没有正则表达式的方法。

我注意到每次字符串a的一部分被b替换时, b将始终是最终字符串的一部分。 所以,你可以从那时起忽略字符串中的b

不仅如此,在用b替换之后,还会有一个“空间”。 不应该在b应该在哪里进行替换。

这些行为加起来看起来很像splitsplit值(在字符串之间创建“空格”),对数组中的每个字符串进行进一步替换,然后将它们连接起来。

例如:

 // Original "cat dog fish dog fish cat" // Replace cat with dog {"", "dog fish dog fish", ""}.join("dog") // Replace dog with fish { "", {"", " fish ", " fish"}.join("fish") "" }.join("dog") // Replace fish with cat { "", { "", {" ", " "}.join("cat"), {" ", ""}.join("cat") }.join("fish") "" }.join("dog") 

到目前为止,最直观的方式(对我来说)是递归地执行此操作:

 public static String replaceWithJointMap(String s, Map map) { // Base case if (map.size() == 0) { return s; } // Get some value in the map to replace Map.Entry pair = map.entrySet().iterator().next(); String replaceFrom = (String) pair.getKey(); String replaceTo = (String) pair.getValue(); // Split the current string with the replaceFrom string // Use split with -1 so that trailing empty strings are included String[] splitString = s.split(Pattern.quote(replaceFrom), -1); // Apply replacements for each of the strings in the splitString HashMap replacementsLeft = new HashMap<>(map); replacementsLeft.remove(replaceFrom); for (int i=0; i 

我不认为这是非常有效的。