Digamos que tengo el siguiente código:
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);
Después de que se ejecute este código, el valor de story
será"Once upon a time, there was a foo and a foo."
Un problema similar ocurre si los reemplacé en el orden opuesto:
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);
El valor de story
será"Once upon a time, there was a bar and a bar."
Mi objetivo es convertirme story
en "Once upon a time, there was a bar and a foo."
¿Cómo podría lograr eso?
swap(String s1, String s2, String s3)
que intercambie todas las apariciones des2
cons3
, y viceversa.Respuestas:
Use el
replaceEach()
método de Apache Commons StringUtils :fuente
null
se pasa.Utiliza un valor intermedio (que aún no está presente en la oración).
Como respuesta a las críticas: si usa una cadena poco común lo suficientemente grande como zq515sqdqs5d5sq1dqs4d1q5dqqé "& é5d4sqjshsjddjhodfqsqc, nvùq ^ µù; d & € sdq: d:;) àçàçlala y usará ese punto, aunque sea poco probable que lo discuta. que un usuario alguna vez ingresará esto. La única manera de saber si un usuario lo hará es conociendo el código fuente y en ese punto usted está con un nivel completamente diferente de preocupaciones.
Sí, tal vez hay formas elegantes de expresiones regulares. Prefiero algo legible que sé que tampoco me explotará.
También reiterando el excelente consejo dado por @David Conrad en los comentarios :
fuente
Puedes probar algo como esto, usando
Matcher#appendReplacement
yMatcher#appendTail
:fuente
foo
,bar
ystory
todos tienen valores desconocidos?"foo"
y"bar"
sustitución de cadenas como el PO tenía en su código, pero el mismo tipo de enfoque podría funcionar bien incluso si no se conocen los valores (que tendría que usarif
/else if
en lugar de unaswitch
dentro de lawhile
-lazo).Pattern.quote
sería útil, o\Q
y\E
.(foo)|(bar)
y luego verificarm.group(1) != null
, para evitar repetir las palabras para que coincidan.Este no es un problema fácil. Y cuantos más parámetros de reemplazo de búsqueda tenga, más complicado será. Tienes varias opciones, dispersas en la paleta de feo-elegante, eficiente-derrochador:
Uso
StringUtils.replaceEach
de Apache Commons como @AlanHay recomendado. Esta es una buena opción si es libre de agregar nuevas dependencias en su proyecto. Puede tener suerte: la dependencia puede estar incluida ya en su proyectoUse un marcador de posición temporal como sugirió @Jeroen y realice el reemplazo en 2 pasos:
Este no es un gran enfoque, por varias razones: debe asegurarse de que las etiquetas utilizadas en el primer paso sean realmente únicas; realiza más operaciones de reemplazo de cadenas de las realmente necesarias
Cree una expresión regular a partir de todos los patrones y use el método con
Matcher
yStringBuffer
como lo sugiere @arshajii . Esto no es terrible, pero tampoco tan bueno, ya que construir la expresión regular es algo hack, e implica loStringBuffer
que pasó de moda hace un tiempo a favorStringBuilder
.Use una solución recursiva propuesta por @mjolka , dividiendo la cadena en los patrones coincidentes y recurriendo en los segmentos restantes. Esta es una buena solución, compacta y bastante elegante. Su debilidad es el potencial de muchas operaciones de subcadena y concatenación, y los límites de tamaño de pila que se aplican a todas las soluciones recursivas.
Divida el texto en palabras y use secuencias de Java 8 para realizar los reemplazos con elegancia como sugirió @msandiford , pero por supuesto eso solo funciona si está de acuerdo con la división en los límites de las palabras, lo que lo hace no adecuado como una solución general
Aquí está mi versión, basada en ideas tomadas de la implementación de Apache . No es simple ni elegante, pero funciona, y debería ser relativamente eficiente, sin pasos innecesarios. En pocas palabras, funciona así: encuentra repetidamente el siguiente patrón de búsqueda coincidente en el texto y usa un
StringBuilder
para acumular los segmentos no y los reemplazos.Pruebas unitarias:
fuente
Busque la primera palabra que se reemplazará. Si está en la cadena, repita en la parte de la cadena antes de la ocurrencia y en la parte de la cadena después de la ocurrencia.
De lo contrario, continúe con la siguiente palabra para ser reemplazado.
Una implementación ingenua podría verse así
Uso de la muestra:
Salida:
Una versión menos ingenua:
Desafortunadamente, Java
String
no tieneindexOf(String str, int fromIndex, int toIndex)
método. He omitido la implementación deindexOf
aquí, ya que no estoy seguro de que sea correcto, pero se puede encontrar en ideone , junto con algunos tiempos difíciles de varias soluciones publicadas aquí.fuente
One-liner en Java 8:
?<=
,?=
): http://www.regular-expressions.info/lookaround.htmlfuente
Aquí hay una posibilidad de secuencias de Java 8 que puede ser interesante para algunos:
Aquí hay una aproximación del mismo algoritmo en Java 7:
fuente
Si desea reemplazar palabras en una oración que están separadas por espacios en blanco como se muestra en su ejemplo, puede usar este algoritmo simple.
Si la división en el espacio no es aceptable, se puede seguir este algoritmo alternativo. Primero debes usar la cadena más larga. Si los hilos son tontos y tontos, primero debes usar tontos y luego tontos.
fuente
Aquí hay una respuesta menos complicada usando Map.
Y el método se llama
La salida es: impresionante es Raffy, Raffy Raffy es increíble increíble
fuente
replaced.replaceAll("Raffy", "Barney");
después de esto lo hará legendario ... espera; Dary !!!Si desea poder manejar múltiples ocurrencias de las cadenas de búsqueda que se reemplazarán, puede hacerlo fácilmente dividiendo la cadena en cada término de búsqueda y luego reemplazándola. Aquí hay un ejemplo:
fuente
Puede lograr su objetivo con el siguiente bloque de código:
Reemplaza las palabras independientemente del orden. Puede extender este principio a un método de utilidad, como:
Que se consumiría como:
fuente
Esto funciona y es simple:
Lo usas así:
Nota: esto cuenta con cadenas que no contienen caracteres
\ufdd0
, que es un carácter reservado permanentemente para uso interno de Unicode (consulte http://www.unicode.org/faq/private_use.html ):No creo que sea necesario, pero si quieres estar absolutamente seguro puedes usar:
fuente
Intercambiando solo una ocurrencia
Si solo hay una aparición de cada una de las cadenas intercambiables en la entrada, puede hacer lo siguiente:
Antes de proceder a cualquier reemplazo, obtenga los índices de las ocurrencias de las palabras. Después de eso solo reemplazamos la palabra encontrada en estos índices, y no todas las ocurrencias. Esta solución utiliza
StringBuilder
y no produce productos intermediosString
similaresString.replace()
.Una cosa a tener en cuenta: si las palabras intercambiables tienen diferentes longitudes, después del primer reemplazo, el segundo índice puede cambiar (si la primera palabra aparece antes que la segunda) exactamente con la diferencia de las 2 longitudes. Al alinear el segundo índice se garantizará que esto funcione incluso si estamos intercambiando palabras con diferentes longitudes.
Intercambio de número arbitrario de ocurrencias
De manera análoga al caso anterior, primero recopilaremos los índices (ocurrencias) de las palabras, pero en este caso será una lista de enteros para cada palabra, no solo una
int
. Para esto utilizaremos el siguiente método de utilidad:Y usando esto, reemplazaremos las palabras con la otra disminuyendo el índice (lo que puede requerir alternar entre las 2 palabras intercambiables) para que ni siquiera tengamos que corregir los índices después de un reemplazo:
fuente
indexOf
coincide puede no tener la misma longitud que la cadena de búsqueda gracias a las idiosincrasias de equivalencia de cadena unicode.String
es una matriz de caracteres y no una matriz de bytes. Todos los métodos deString
yStringBuilder
funcionan en caracteres no en bytes, que están "libres de codificación". Por lo tanto, lasindexOf
coincidencias tienen exactamente la misma longitud (carácter) que las cadenas de búsqueda.ä
puede codificarse como un único punto de código o comoa
seguido de una combinación¨
. También hay algunos puntos de código que se ignoran, como las uniones de ancho cero (no). No importa si la cadena consiste en bytes, caracteres o lo que sea, sino qué reglas de comparaciónindexOf
utiliza. Podría usar simplemente la comparación de unidad de código por unidad de código ("Ordinal") o podría implementar equivalencia unicode. No sé cuál eligió Java."ab\u00ADc".IndexOf("bc")
regresa1
en .net haciendo coincidir la cadena de dos caracteresbc
con una cadena de tres caracteres."ab\u00ADc".indexOf("bc")
devuelve lo-1
que significa que"bc"
no se encontró en"ab\u00ADc"
. Por lo tanto, sigue en pie que en Java el algoritmo anterior funciona, lasindexOf()
coincidencias tienen exactamente la misma longitud (caracteres) que las cadenas de búsqueda, yindexOf()
solo informa coincidencias si coinciden las coincidencias (puntos de código).Es fácil escribir un método para hacer esto usando
String.regionMatches
:Pruebas:
Salida:
No es inmediatamente obvio, pero una función como esta todavía puede depender del orden en que se especifican los reemplazos. Considerar:
Salida:
Pero invierta los reemplazos:
Salida:
¡Uy! :)
Por lo tanto, a veces es útil asegurarse de buscar la coincidencia más larga (como lo hace la
strtr
función de PHP , por ejemplo). Esta versión del método hará eso:Tenga en cuenta que los métodos anteriores distinguen entre mayúsculas y minúsculas. Si necesita una versión que no distinga entre mayúsculas y minúsculas, es fácil modificar lo anterior porque
String.regionMatches
puede tomar unignoreCase
parámetro.fuente
Si no desea ninguna dependencia, simplemente puede usar una matriz que permita un cambio único. Esta no es la solución más eficiente, pero debería funcionar.
Entonces, debería funcionar.
fuente
Está realizando múltiples operaciones de búsqueda de reemplazo en la entrada. Esto producirá resultados no deseados cuando las cadenas de reemplazo contengan cadenas de búsqueda. Considere el ejemplo foo-> bar, bar-foo, aquí están los resultados para cada iteración:
Debe realizar el reemplazo en una iteración sin volver atrás. Una solución de fuerza bruta es la siguiente:
Una función como
String.indexOfAny(String[]) -> int[]{index, whichString}
sería útil. Aquí hay un ejemplo (no el más eficiente):Algunas pruebas:
Demo en IDEONE
Demo en IDEONE, código alternativo
fuente
Siempre puede reemplazarlo con una palabra que está seguro que no aparecerá en ningún otro lugar de la cadena, y luego haga el segundo reemplazo más tarde:
Tenga en cuenta que esto no funcionará correctamente si
"StringYouAreSureWillNeverOccur"
ocurre.fuente
Considere usar StringBuilder
Luego almacene el índice donde debe comenzar cada cadena. Si usa un carácter de marcador de posición en cada posición, elimínelo e inserte la cadena de usuarios. Luego puede asignar la posición final agregando la longitud de la cadena a la posición inicial.
fuente
Lo que solo puedo compartir es mi propio método.
Puedes usar un temporal
String temp = "<?>";
oString.Format();
Este es mi código de ejemplo creado en la aplicación de consola a través de C# - "Solo idea, no respuesta exacta" .
O también puedes usar el
String.Format();
Salida:
time upon a Once, there was a bar and a foo.
fuente
temp
de"_"
a<?>
. Pero si es necesario, lo que puede hacer es agregar otro parámetro al método que cambiará la temperatura. - "es mejor mantenerlo simple ¿verdad?"Aquí está mi versión, que está basada en palabras:
fuente
Un poco complicado, pero debes hacer algunas verificaciones más.
1.convertir cadena a matriz de caracteres
2.seleccione la temperatura y reemplace
foo
conbar
ybar
confoo
ya que no hay posibilidades de obtener una cuerda reemplazable nuevamente.fuente
Bueno, la respuesta más corta es ...
fuente
Usando la respuesta que se encuentra aquí , puede encontrar todas las ocurrencias de las cadenas con las que desea reemplazar.
Entonces, por ejemplo, ejecuta el código en la respuesta SO anterior. Cree dos tablas de índices (digamos que bar y foo no aparecen solo una vez en su cadena) y puede trabajar con esas tablas para reemplazarlas en su cadena.
Ahora para reemplazar en ubicaciones de índice específicas puede usar:
Mientras que
pos
es el índice donde comienzan sus cadenas (de las tablas de índice que cité anteriormente). Digamos que creó dos tablas de índices para cada una. Vamos a llamarlosindexBar
yindexFoo
.Ahora, al reemplazarlos, simplemente puede ejecutar dos bucles, uno para cada reemplazo que desee realizar.
Del mismo modo otro bucle para
indexFoo
.Esto puede no ser tan eficiente como otras respuestas aquí, pero es más fácil de entender que Maps u otras cosas.
Esto siempre le daría el resultado que desea y para múltiples posibles ocurrencias de cada cadena. Siempre que almacene el índice de cada aparición.
Además, esta respuesta no necesita recurrencia ni dependencias externas. En lo que respecta a la complejidad, es probable que sea O (n al cuadrado), mientras que n es la suma de las ocurrencias de ambas palabras.
fuente
Desarrollé este código resolverá el problema:
En el uso principal
change(story,word2,word1).
fuente
fuente