En primer lugar, una manifestación del problema:
String s = "I have three cats and two dogs.";
s = s.replace("cats", "dogs")
.replace("dogs", "budgies");
System.out.println(s);
Esto está destinado a reemplazar cats => dogs and dogs => budgies, pero la sustitución secuencial opera sobre el resultado de la r anterior eplacement, por lo que la salida desafortunada es:
Tengo tres periquitos y dos periquitos.
Aquí está mi implementación de un método de reemplazo simultáneo. Es fácil escribir usando 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();
}
Pruebas:
String s = "I have three cats and two dogs.";
s = simultaneousReplace(s,
"cats", "dogs",
"dogs", "budgies");
System.out.println(s);
Salida:
tengo tres perros y dos periquitos.
Además, a veces es útil cuando se realiza un reemplazo simultáneo, para asegurarse de buscar la coincidencia más larga. (La función strtr
de PHP hace esto, por ejemplo.) Aquí está mi implementación para eso:
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();
}
¿Por qué necesita esto? Ejemplo es el siguiente:
String truth = "Java is to JavaScript";
truth += " as " + simultaneousReplaceLongest(truth,
"Java", "Ham",
"JavaScript", "Hamster");
System.out.println(truth);
Salida:
Java es tener JavaScript como jamón es a Hamster
Si hubiéramos utilizado simultaneousReplace
en lugar de simultaneousReplaceLongest
, la salida habría tenido "HamScript" en lugar de "Hamster" :)
Tenga en cuenta que los métodos anteriores distinguen entre mayúsculas y minúsculas. Si necesita versiones que no distingan entre mayúsculas y minúsculas, es fácil modificar lo anterior porque String.regionMatches
puede tomar un parámetro ignoreCase
.
Tema relacionado - http://stackoverflow.com/questions/2049528/java-best-way-for-string-find-and-replace – adatapost