2008-09-12 16 views
271

¿Tiene Java una forma integrada de escapar del texto arbitrario para que se pueda incluir 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.Cómo escanear texto para la expresión regular en Java

Respuesta

397

Desde Java 1.5, yes:

Pattern.quote("$5"); 
+68

por favor, no es que esto no escapa a la propia cadena, pero lo envuelve el uso de '\ Q' y' \ E'. Esto puede conducir a resultados inesperados, por ejemplo 'Pattern.quote (" *. Wav "). ReplaceAll (" * ",". * ")' Dará como resultado '\ Q. *. Wav \ E' y no'. * \. wav', como era de esperar. – Paramaeleon

+9

@Paramaeleon ¿Por qué esperarías que foo (x) .bar() == x.bar()? – Michael

+6

@Paramaeleon Creo que está malinterpretando el caso de uso. – vikingsteve

13

Creo que lo que está buscando es \Q$5\E. También vea Pattern.quote(s) introducido en Java5.

Ver Pattern javadoc para más detalles.

+0

Tengo curiosidad si hay alguna diferencia entre esto y el uso de la bandera LITERAL, ya que el javadoc dice que no hay un indicador incrustado para encender y apagar LITERAL: http: // java. sun.com/j2se/1.5.0/docs/api/java/util/regex/Pattern.html#LITERAL –

+14

Tenga en cuenta que usar literalmente \ Q y \ E solo está bien si conoce su entrada. Pattern.quote (s) también manejará el caso donde su texto realmente contiene estas secuencias. –

92

Diferencia entre Pattern.quote y Matcher.quoteReplacement no estaba claro para mí antes de ver siguiente ejemplo

s.replaceFirst(Pattern.quote("text to replace"), 
       Matcher.quoteReplacement("replacement text")); 
+28

Específicamente, 'Pattern.quote' reemplaza los caracteres especiales en las cadenas de búsqueda de expresiones regulares, como. | +() Etc, y' Matcher.quoteReplacement' reemplaza los caracteres especiales en las cadenas de reemplazo, como \ 1 para las referencias retrospectivas. – Steven

+7

No estoy de acuerdo. Pattern.quote envuelve su argumento con \ Q y \ E. No escapa a los personajes especiales. –

+5

Matcher.quoteReplacement ("4 $ &% $") produce "4 \ $ &% \ $". Se escapa de los personajes especiales. –

10

primer lugar, si

  • utiliza replaceAll()
  • usted no utiliza Matcher .quoteReplacement()
  • el texto que debe sustituirse en incluye un $ 1

no pondrá un 1 al final. Examinará la expresión regular de búsqueda para el primer grupo coincidente y 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.

Frecuentemente enchufo largas cadenas de texto en archivos .properties, luego genero sujetos y cuerpos de correo electrónico a partir de esos. De hecho, esta parece ser la forma predeterminada de hacer i18n en Spring Framework. Puse etiquetas XML, como marcadores de posición, en las cadenas y utilizo replaceAll() para reemplazar las etiquetas XML con los valores en tiempo de ejecución.

Me encontré con un problema donde un usuario ingresa una cifra de dólares y centavos, con un signo de dólar. replaceAll() se atragantó con ella, con la siguiente aparecer en un stracktrace:

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 ha entrado en "$ 3" en algún lugar de su entrada y replaceAll() fue en busca de 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)); 

resuelto el problema. El usuario puede poner cualquier tipo de caracteres, incluidos los signos de dólar, sin problema. Se comportó exactamente de la manera esperada.

4

Para tener un patrón protegido, puede reemplazar todos los símbolos por "\\\\", excepto los dígitos y las letras. Y después de eso, puede poner en ese patrón protegido sus símbolos especiales para que este patrón funcione no como un texto estúpido, sino como un patten, pero el suyo propio. 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"); 
    } 
} 
+0

No tiene que escapar espacios. Entonces puede chagne su patrón a "([^ a-zA-z0-9])". –

+4

Pequeño error tipográfico, grandes consecuencias: "([^ a-zA-z0-9])" tampoco coincide (es decir, no escapa) [, \,],^que ciertamente desea haber escapado! 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

19

Puede ser demasiado tarde para responder, pero también se puede utilizar Pattern.LITERAL, que tendría en cuenta todos los caracteres especiales durante el formateo:

Pattern.compile(textToFormat, Pattern.LITERAL); 
+0

Es especialmente agradable porque puedes combinarlo con 'Pattern.CASE_INSENSITIVE' – mjjaniec

1

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

El Pattern.quote() funciona muy bien. Encierra la oración con los caracteres "\ Q" y "\ E", y si lo hace, escapa "\ Q" y "\ E". Sin embargo, si lo que necesita hacer una verdadera expresión regular escape (o la costumbre de escapar), puede utilizar 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")); 
Cuestiones relacionadas