String.replaceTodas las barras diagonales inversas dobles

122

Estoy tratando de convertir el String \something\en el String \\something\\uso replaceAll, pero seguir recibiendo todo tipo de errores. Pensé que esta era la solución:

theString.replaceAll("\\", "\\\\");

Pero esto da la siguiente excepción:

java.util.regex.PatternSyntaxException: Unexpected internal error near index 1
Frank Groeneveld
fuente

Respuestas:

204

El String#replaceAll()interpreta el argumento como una expresión regular . El \es un personaje de escape en ambos String y regex. Debes escapar dos veces para regex:

string.replaceAll("\\\\", "\\\\\\\\");

Pero no necesariamente necesita expresiones regulares para esto, simplemente porque desea un reemplazo exacto de carácter por carácter y no necesita patrones aquí. Entonces String#replace()debería ser suficiente:

string.replace("\\", "\\\\");

Actualización : según los comentarios, parece que desea utilizar la cadena en el contexto de JavaScript. Quizás sea mejor usarlo StringEscapeUtils#escapeEcmaScript()para cubrir más personajes.

BalusC
fuente
En realidad, se usa en un JavaScript AST que debe convertirse de nuevo a la fuente. Tu solución funciona. ¡Gracias!
Frank Groeneveld
2
Si quiere usarlo de String#replaceAll()todos modos, puede citar la cadena de reemplazo con Matcher # quoteReplacement () :theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));
phse
Matcher.quoteReplacement (...) es una buena manera! ¡Por favor vea la respuesta de Pshemo!
Hartmut P.
14

Para evitar este tipo de problemas, puede usar replace(que toma una cadena simple) en lugar de replaceAll(que toma una expresión regular). Aún necesitará escapar de las barras diagonales inversas, pero no de la manera salvaje requerida con las expresiones regulares.

Fabian Steeg
fuente
10

TLDR: utilizar theString = theString.replace("\\", "\\\\");en su lugar.


Problema

replaceAll(target, replacement)usa sintaxis de expresión regular (regex) para targety parcialmente para replacement.

El problema es que \es un carácter especial en regex (se puede usar como \dpara representar un dígito) y en String literal (se puede usar "\n"para representar un separador de línea o \"para escapar del símbolo de comillas dobles que normalmente representaría el final del literal de cadena).

En ambos casos, para crear un \símbolo, podemos escapar de él (hacerlo literal en lugar de caracteres especiales) colocando adicional \antes (como escapamos "en literales de cadena a través de \").

Por lo tanto, para targetregexar el \símbolo que representa tendrá que mantenerse \\, y el literal de cadena que representa dicho texto deberá parecerse "\\\\".

Entonces escapamos \dos veces:

  • una vez en regex \\
  • una vez en cadena literal "\\\\"(cada uno \se representa como "\\").

En caso de replacement \que también sea especial allí. Nos permite escapar de otros caracteres especiales $que a través de la $xnotación, nos permite usar una parte de los datos que coinciden con expresiones regulares y que se mantienen al capturar el grupo indexado x, ya que , como "012".replaceAll("(\\d)", "$1$1")coincidirá con cada dígito, colocarlo en el grupo de captura 1 y $1$1reemplazarlo con sus dos copias (lo duplicará) resultando en "001122".

Entonces, de nuevo, para dejar de replacementrepresentar \literal, necesitamos escapar de él con adicional \que significa que:

  • el reemplazo debe contener dos caracteres de barra invertida \\
  • y literal de cadena que representa \\parece"\\\\"

PERO ya que queremos replacementmantener dos barras invertidas necesitaremos "\\\\\\\\"(cada una \representada por una "\\\\").

Entonces la versión con replaceAllpuede verse como

replaceAll("\\\\", "\\\\\\\\");

Manera más fácil

Para hacer la vida más fácil, Java proporciona herramientas para escapar automáticamente texto targety replacementpartes. Entonces ahora podemos enfocarnos solo en cadenas y olvidarnos de la sintaxis de expresiones regulares:

replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement))

que en nuestro caso puede parecer

replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"))

Aun mejor

Si realmente no necesitamos soporte de sintaxis de expresiones regulares, no nos involucremos replaceAllen absoluto. En cambio, vamos a usar replace. Ambos métodos reemplazarán todos los target s, pero replaceno involucran sintaxis de expresiones regulares. Entonces podrías simplemente escribir

theString = theString.replace("\\", "\\\\");
Pshemo
fuente
7

Tendrá que escapar de la barra diagonal inversa (escapado) en el primer argumento, ya que es una expresión regular. Reemplazo (segundo argumento: consulte Matcher # replaceAll (String) ) también tiene su significado especial de barras invertidas, por lo que deberá reemplazarlas para:

theString.replaceAll("\\\\", "\\\\\\\\");
sfussenegger
fuente
3

Sí ... cuando el compilador de expresiones regulares ve el patrón que le ha dado, solo ve una barra invertida (ya que el lexer de Java ha convertido la doble retroceso en una sola). Es necesario sustituir "\\\\"con "\\\\", aunque no lo crean! Java realmente necesita una buena sintaxis de cadena sin formato.

Jonathan Feinberg
fuente