Cómo escapar de texto para expresiones regulares en Java

320

¿Tiene Java una forma integrada de escapar del texto arbitrario para que pueda incluirse en una expresión regular? Por ejemplo, si mis usuarios ingresan "$ 5", me gustaría hacer coincidir exactamente eso en lugar de un "5" después del final de la entrada.

Mate
fuente

Respuestas:

450

Desde Java 1.5, sí :

Pattern.quote("$5");
Mike Stone
fuente
88
Por favor, no es que esto no escape de la cadena en sí, sino que la envuelve usando \Qy \E. Esto puede conducir a resultados inesperados, por ejemplo, Pattern.quote("*.wav").replaceAll("*",".*")dará como resultado \Q.*.wav\Ey no .*\.wav, como es de esperar.
Matthias Ronge
11
@Paramaeleon ¿Por qué esperarías que foo (x) .bar () == x.bar ()?
Michael
77
@Paramaeleon Creo que está malinterpretando el caso de uso.
vikingsteve
18
Solo quiero señalar que esta forma de escapar también se aplica a las expresiones que introduces después . Esto puede ser sorprendente. Si lo haces "mouse".toUpperCase().replaceAll("OUS","ic"), volverá MicE. Usted would't espera que devuelva MICEporque no aplicó toUpperCase()sobre ic. En mi ejemplo, también quote()se aplica en el .*insertet replaceAll(). Tienes que hacer otra cosa, tal vez .replaceAll("*","\\E.*\\Q")funcionaría, pero eso es contradictorio.
Matthias Ronge
2
@Paramaleon Si funcionó agregando escapes individuales, su ejemplo inicial todavía no haría lo que deseaba ... si escapara caracteres individualmente, se convertiría *.waven el patrón de \*\.wavexpresiones regulares , y el replaceAll lo convertiría en \.*\.wav, lo que significa que sería coincide con archivos cuyo nombre consiste en un número arbitrario de períodos seguidos de .wav. Lo más probable replaceAll("\\*", ".*")es que lo hubieras necesitado si se hubieran ido con la implementación más frágil que se basa en reconocer todos los caracteres de expresiones regulares activos posibles y escapar de ellos individualmente ... ¿sería eso mucho más fácil?
Theodore Murdock
112

La diferencia entre Pattern.quotey Matcher.quoteReplacementno estaba clara para mí antes de ver el siguiente ejemplo

s.replaceFirst(Pattern.quote("text to replace"), 
               Matcher.quoteReplacement("replacement text"));
Pavel Feldman
fuente
29
Específicamente, Pattern.quotereemplaza caracteres especiales en cadenas de búsqueda de expresiones regulares, como. | + (), Etc., y Matcher.quoteReplacementreemplaza caracteres especiales en cadenas de reemplazo, como \ 1 para referencias posteriores.
Steven
99
No estoy de acuerdo Pattern.quote envuelve su argumento con \ Q y \ E. No escapa a caracteres especiales.
David Medinets
55
Matcher.quoteReplacement ("4 $ &% $") produce "4 \ $ &% \ $". Se escapa de los caracteres especiales.
David Medinets
44
En otras palabras: quoteReplacementsolo se preocupa por los dos símbolos $y \ que, por ejemplo, se pueden usar en cadenas de reemplazo como referencias $1o \1. Por lo tanto, no debe usarse para escapar / citar una expresión regular.
SebastianH
1
Increíble. Aquí está un ejemplo donde queremos sustituir $Group$con T$UYO$HI. El $símbolo es especial tanto en el patrón como en el reemplazo:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
Arun
29

Puede ser demasiado tarde para responder, pero también puede usarlo Pattern.LITERAL, lo que ignoraría todos los caracteres especiales al formatear:

Pattern.compile(textToFormat, Pattern.LITERAL);
Androidme
fuente
Es especialmente agradable porque puedes combinarlo conPattern.CASE_INSENSITIVE
mjjaniec
13

Creo que lo que buscas es \Q$5\E. Ver también Pattern.quote(s)introducido en Java5.

Ver Patrón javadoc para más detalles.

Rob Oxspring
fuente
Tengo curiosidad por saber si hay alguna diferencia entre esto y usar el indicador LITERAL, ya que el javadoc dice que no hay un indicador incrustado para activar y desactivar LITERAL: java.sun.com/j2se/1.5.0/docs/api/java/ util / regex / ...
Chris Mazzola
15
Tenga en cuenta que, literalmente, usar \ Q y \ E solo está bien si conoce su entrada. Pattern.quote (s) también manejará el caso en el que su texto realmente contenga estas secuencias.
Jeremy Huiskamp
10

En primer lugar, si

  • usa replaceAll ()
  • NO use Matcher.quoteReplacement ()
  • el texto a ser sustituido incluye $ 1

no pondrá un 1 al final. Verá la expresión regular de búsqueda para el primer grupo coincidente y el sub ESO en. Eso es lo que significa $ 1, $ 2 o $ 3 en el texto de reemplazo: grupos coincidentes del patrón de búsqueda.

Con frecuencia conecto cadenas largas de texto en archivos .properties, luego genero asuntos y cuerpos de correo electrónico a partir de ellos. De hecho, esta parece ser la forma predeterminada de hacer i18n en Spring Framework. Pongo etiquetas XML, como marcadores de posición, en las cadenas y uso replaceAll () para reemplazar las etiquetas XML con los valores en tiempo de ejecución.

Me encontré con un problema en el que un usuario ingresaba una cifra de dólares y centavos, con un signo de dólar. replaceAll () se atragantó, con lo siguiente apareciendo en una pista de seguimiento:

java.lang.IndexOutOfBoundsException: No group 3
at java.util.regex.Matcher.start(Matcher.java:374)
at java.util.regex.Matcher.appendReplacement(Matcher.java:748)
at java.util.regex.Matcher.replaceAll(Matcher.java:823)
at java.lang.String.replaceAll(String.java:2201)

En este caso, el usuario había ingresado "$ 3" en algún lugar de su entrada y replaceAll () fue a buscar en la expresión regular de búsqueda para el tercer grupo coincidente, no encontró uno y vomitó.

Dado:

// "msg" is a string from a .properties file, containing "<userInput />" among other tags
// "userInput" is a String containing the user's input

reemplazando

msg = msg.replaceAll("<userInput \\/>", userInput);

con

msg = msg.replaceAll("<userInput \\/>", Matcher.quoteReplacement(userInput));

resuelve el problema. El usuario puede ingresar cualquier tipo de caracteres, incluidos los signos de dólar, sin problema. Se comportó exactamente de la manera que cabría esperar.

Meower68
fuente
6

Para tener un patrón protegido, puede reemplazar todos los símbolos con "\\\\", excepto dígitos y letras. Y después de eso, puede poner en ese patrón protegido sus símbolos especiales para hacer que este patrón funcione no como un texto citado estúpido, sino realmente como un patrón, sino el suyo. Sin símbolos especiales de usuario.

public class Test {
    public static void main(String[] args) {
        String str = "y z (111)";
        String p1 = "x x (111)";
        String p2 = ".* .* \\(111\\)";

        p1 = escapeRE(p1);

        p1 = p1.replace("x", ".*");

        System.out.println( p1 + "-->" + str.matches(p1) ); 
            //.*\ .*\ \(111\)-->true
        System.out.println( p2 + "-->" + str.matches(p2) ); 
            //.* .* \(111\)-->true
    }

    public static String escapeRE(String str) {
        //Pattern escaper = Pattern.compile("([^a-zA-z0-9])");
        //return escaper.matcher(str).replaceAll("\\\\$1");
        return str.replaceAll("([^a-zA-Z0-9])", "\\\\$1");
    }
}
Niño de moscú
fuente
No tienes que escapar de los espacios. Entonces puede cambiar su patrón a "([^ a-zA-z0-9])".
Erel Segal-Halevi
55
Pequeño error tipográfico, grandes consecuencias: "([^ a-zA-z0-9])" tampoco coincide (es decir, no escapa) [, \,], ^ del que sin duda quiere escapar. El error tipográfico es la segunda 'z' que debería ser una 'Z', de lo contrario, se incluye todo, desde ASCII 65 hasta ASCII 122
Zefiro
3

Pattern.quote ("blabla") funciona muy bien.

Pattern.quote () funciona muy bien. Encierra la oración con los caracteres " \ Q " y " \ E ", y si se escapa "\ Q" y "\ E". Sin embargo, si necesita hacer un escape de expresión regular real (o un escape personalizado), puede usar este código:

String someText = "Some/s/wText*/,**";
System.out.println(someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));

Este método devuelve: Algunos / \ s / wText * / \, **

Código por ejemplo y pruebas:

String someText = "Some\\E/s/wText*/,**";
System.out.println("Pattern.quote: "+ Pattern.quote(someText));
System.out.println("Full escape: "+someText.replaceAll("[-\\[\\]{}()*+?.,\\\\\\\\^$|#\\\\s]", "\\\\$0"));
Adam111p
fuente
-2

El símbolo ^ (Negación) se usa para hacer coincidir algo que no está en el grupo de caracteres.

Este es el enlace a las expresiones regulares

Aquí está la información de la imagen sobre la negación:

Información sobre negación

Akhil Kathi
fuente