Сначала демонстрация проблемы:
String s = "I have three cats and two dogs.";
s = s.replace("cats", "dogs")
.replace("dogs", "budgies");
System.out.println(s);
Это предназначено для замены кошек => собак и собак => волнистых попугайчиков, но последовательная замена действует на результат предыдущей замены, поэтому неудачный вывод:
У меня три волнистых попугая и два волнистых попугая.
Вот моя реализация метода одновременной замены. Это легко написать, используя 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();
int numPairs = pairs.length / 2;
outer:
for (int i = 0; i < subject.length(); i++) {
for (int j = 0; j < numPairs; j++) {
String find = pairs[j * 2];
if (subject.regionMatches(i, find, 0, find.length())) {
sb.append(pairs[j * 2 + 1]);
i += find.length() - 1;
continue outer;
}
}
sb.append(subject.charAt(i));
}
return sb.toString();
}
Тестирование:
String s = "I have three cats and two dogs.";
s = simultaneousReplace(s,
"cats", "dogs",
"dogs", "budgies");
System.out.println(s);
Выход:
У меня три собаки и два волнистых попугая.
Кроме того, иногда полезно выполнять одновременную замену, чтобы убедиться в том, что найдено самое длинное совпадение. (Например, функция PHP strtr
делает это.) Вот моя реализация для этого:
public static String simultaneousReplaceLongest(String subject, String... pairs) {
if (pairs.length % 2 != 0) throw new IllegalArgumentException(
"Strings to find and replace are not paired.");
StringBuilder sb = new StringBuilder();
int numPairs = pairs.length / 2;
for (int i = 0; i < subject.length(); i++) {
int longestMatchIndex = -1;
int longestMatchLength = -1;
for (int j = 0; j < numPairs; j++) {
String find = pairs[j * 2];
if (subject.regionMatches(i, find, 0, find.length())) {
if (find.length() > longestMatchLength) {
longestMatchIndex = j;
longestMatchLength = find.length();
}
}
}
if (longestMatchIndex >= 0) {
sb.append(pairs[longestMatchIndex * 2 + 1]);
i += longestMatchLength - 1;
} else {
sb.append(subject.charAt(i));
}
}
return sb.toString();
}
Зачем вам это нужно? Пример следует:
String truth = "Java is to JavaScript";
truth += " as " + simultaneousReplaceLongest(truth,
"Java", "Ham",
"JavaScript", "Hamster");
System.out.println(truth);
Выход:
Ява для JavaScript, как Хэм для Хомяка
Если бы мы использовали simultaneousReplace
вместо simultaneousReplaceLongest
, на выходе был бы "HamScript" вместо "Hamster":)
Обратите внимание, что вышеупомянутые методы чувствительны к регистру. Если вам нужны версии без учета регистра, легко изменить вышеприведенное, потому что String.regionMatches
может принимать параметр ignoreCase
.