Come sfuggire al testo per l'espressione regolare in Java


320

Java ha un modo integrato per sfuggire al testo arbitrario in modo che possa essere incluso in un'espressione regolare? Ad esempio, se i miei utenti inseriscono "$ 5", vorrei corrispondere esattamente a questo anziché a "5" dopo la fine dell'input.

Risposte:


450

Da Java 1.5, sì :

Pattern.quote("$5");

88
Per favore non che questo non sfugga alla stringa stessa, ma la avvolge usando \Qe \E. Ciò può portare a risultati imprevisti, ad esempio, Pattern.quote("*.wav").replaceAll("*",".*")si tradurrà in \Q.*.wav\Ee non .*\.wav, come ci si potrebbe aspettare.
Matthias Ronge,

11
@Paramaeleon Perché dovresti aspettarti che foo (x) .bar () == x.bar ()?
Michael,

7
@Paramaeleon Penso che tu stia fraintendendo il caso d'uso.
vikingsteve,

18
Voglio solo far notare che questo modo di scappare si applica anche alle espressioni che introduci in seguito . Questo può essere sorprendente. Se lo fai "mouse".toUpperCase().replaceAll("OUS","ic"), tornerà MicE. Si would't aspetta per tornare MICEperché non hai applica toUpperCase()su ic. Nel mio esempio quote()viene applicato anche .*sull'inserto replaceAll(). Devi fare qualcos'altro, forse .replaceAll("*","\\E.*\\Q")funzionerebbe, ma è controintuitivo.
Matthias Ronge,

2
@Paramaleon Se funzionasse aggiungendo singole escape, il tuo esempio iniziale non farebbe comunque quello che volevi ... se fosse sfuggito ai caratteri singolarmente, si sarebbe trasformato *.wavnel modello regex \*\.wave il sostituire All avrebbe trasformato in \.*\.wav, nel senso che avrebbe confronta i file il cui nome è costituito da un numero arbitrario di periodi seguiti da .wav. Probabilmente avresti dovuto farlo replaceAll("\\*", ".*")se fossero andati con l'implementazione più fragile che si basa sul riconoscimento di tutti i possibili caratteri regex attivi e sulla loro fuga individuale ... Sarebbe molto più facile?
Theodore Murdock,

112

La differenza tra Pattern.quotee Matcher.quoteReplacementnon mi era chiara prima di vedere l'esempio seguente

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

29
In particolare, Pattern.quotesostituisce i caratteri speciali nelle stringhe di ricerca regex, come. | + () Ecc., E Matcher.quoteReplacementsostituisce i caratteri speciali nelle stringhe di sostituzione, come \ 1 per i riferimenti indietro.
Steven,

9
Non sono d'accordo Pattern.quote racchiude il suo argomento con \ Q e \ E. Non sfugge ai caratteri speciali.
David Medinets,

5
Matcher.quoteReplacement ("4 $ &% $") produce "4 \ $ &% \ $". Sfugge ai personaggi speciali.
David Medinets,

4
In altre parole: quoteReplacementsi preoccupa solo dei due simboli $e \ che, ad esempio, possono essere utilizzati nelle stringhe di sostituzione come riferimenti indietro $1o \1. Pertanto non deve essere utilizzato per sfuggire / citare un regex.
SebastianH,

1
Eccezionale. Ecco un esempio in cui vogliamo sostituire $Group$con T$UYO$HI. Il $simbolo è speciale sia nel disegno e nella sostituzione:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
Arun

29

Potrebbe essere troppo tardi per rispondere, ma puoi anche usare Pattern.LITERAL, che ignorerebbe tutti i caratteri speciali durante la formattazione:

Pattern.compile(textToFormat, Pattern.LITERAL);

È particolarmente bello perché puoi combinarlo conPattern.CASE_INSENSITIVE
mjjaniec il

13

Penso che quello che cerchi sia \Q$5\E. Vedi anchePattern.quote(s) introdotto in Java5.

Vedi Pattern javadoc per i dettagli.


Sono curioso di sapere se c'è qualche differenza tra questo e l'uso del flag LITERAL, dal momento che javadoc dice che non c'è alcun flag incorporato per accendere e spegnere LITERAL: java.sun.com/j2se/1.5.0/docs/api/java/ util / regex /…
Chris Mazzola il

15
Nota che usare letteralmente \ Q e \ E va bene solo se conosci i tuoi input. Pattern.quote (s) gestirà anche il caso in cui il testo contiene effettivamente queste sequenze.
Jeremy Huiskamp,

10

Prima di tutto, se

  • si utilizza replAll ()
  • NON usi Matcher.quoteReplacement ()
  • il testo da sostituire include un $ 1

non metterà un 1 alla fine. Esaminerà la regex di ricerca per il primo gruppo corrispondente e il sottotitolo in. Ecco cosa significano $ 1, $ 2 o $ 3 nel testo sostitutivo: gruppi corrispondenti dal modello di ricerca.

Inserisco spesso lunghe stringhe di testo in file .properties, quindi genera oggetti e corpi di posta elettronica da quelli. In effetti, questo sembra essere il modo predefinito di fare i18n in Spring Framework. Inserisco i tag XML, come segnaposto, nelle stringhe e utilizzo replaceAll () per sostituire i tag XML con i valori in fase di esecuzione.

Ho riscontrato un problema in cui un utente inseriva una cifra in dollari e centesimi, con un segno di dollaro. replaceAll () soffocato su di esso, con il seguente che appare in una 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)

In questo caso, l'utente ha inserito "$ 3" da qualche parte nel suo input e sostituisce All () è andato alla ricerca nella regex di ricerca per il terzo gruppo corrispondente, non l'ha trovato e ha vomitato.

Dato:

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

sostituzione

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

con

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

problema risolto. L'utente può inserire qualsiasi tipo di carattere, incluso il simbolo del dollaro, senza problemi. Si è comportato esattamente come ti aspetteresti.


6

Per avere un motivo protetto è possibile sostituire tutti i simboli con "\\\\", tranne cifre e lettere. E dopo puoi inserire in quello schema protetto i tuoi simboli speciali per far funzionare questo schema non come stupido testo tra virgolette, ma davvero come uno zoccolo, ma il tuo. Senza simboli speciali dell'utente.

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");
    }
}

Non devi fuggire dagli spazi. Quindi puoi chagne il tuo schema a "([^ a-zA-z0-9])".
Erel Segal-Halevi,

5
Piccolo errore di battitura, grandi conseguenze: "([[a-zA-z0-9])" non corrisponde (vale a dire non fuga) [, \,], ^ che sicuramente vorresti essere sfuggito! L'errore di battitura è la seconda 'z' che dovrebbe essere una 'Z', altrimenti tutto è compreso da ASCII 65 a ASCII 122
Zefiro

3

Pattern.quote ("blabla") funziona bene.

Pattern.quote () funziona bene. Racchiude la frase con i caratteri " \ Q " e " \ E " e se sfugge a "\ Q" e "\ E". Tuttavia, se devi eseguire l'escaping di un'espressione regolare reale (o l'escaping personalizzato), puoi utilizzare questo codice:

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

Questo metodo restituisce: Some / \ s / wText * / \, **

Codice per esempio e test:

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

-2

^ Il simbolo (Negazione) viene usato per abbinare qualcosa che non è nel gruppo di caratteri.

Questo è il link alle espressioni regolari

Ecco le informazioni sull'immagine relative alla negazione:

Informazioni sulla negazione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.