Jak zmienić tekst na wyrażenie regularne w Javie

320

Czy Java ma wbudowaną metodę ucieczki przed dowolnym tekstem, aby można go było uwzględnić w wyrażeniu regularnym? Na przykład, jeśli moi użytkownicy wprowadzą „5 $”, chciałbym dopasować to dokładnie zamiast „5” po zakończeniu wprowadzania.

Matt
źródło

Odpowiedzi:

450

Od wersji Java 1.5 tak :

Pattern.quote("$5");
Mike Stone
źródło
88
Proszę nie, żeby to nie unikało samego łańcucha, ale owija go za pomocą \Qi \E. Może to prowadzić do nieoczekiwanych rezultatów, na przykład Pattern.quote("*.wav").replaceAll("*",".*")skutkować \Q.*.wav\Ei nie .*\.wav, jak można się spodziewać.
Matthias Ronge
11
@Paramaeleon Dlaczego miałbyś oczekiwać tego foo (x) .bar () == x.bar ()?
Michael
7
@Paramaeleon Myślę, że nie rozumiesz przypadku użycia.
vikingsteve
18
Chciałbym tylko zaznaczyć, że ten sposób ucieczki dotyczy ucieczki także w wyrażeniach, które wprowadzasz później . To może być zaskakujące. Jeśli to zrobisz "mouse".toUpperCase().replaceAll("OUS","ic"), wróci MicE. Would't można oczekiwać, że do powrotu MICE, ponieważ nie stosuje się toUpperCase()na ic. W moim przykładzie quote()jest również stosowany na .*wstawce replaceAll(). Musisz zrobić coś innego, być .replaceAll("*","\\E.*\\Q")może zadziałałoby, ale to sprzeczne z intuicją.
Matthias Ronge
2
@Paramaleon Gdyby zadziałało poprzez dodanie indywidualnych znaków ucieczki, twój początkowy przykład nadal nie zrobiłby tego, co chciałeś ... gdyby osobno unikał znaków, zmieniłby *.wavsię w wzorzec wyrażenia regularnego \*\.wav, a replaceAll zmieniłby go \.*\.wav, co oznaczałoby, że dopasuj pliki, których nazwa składa się z dowolnej liczby kropek, po których następuje .wav. Najprawdopodobniej byś tego potrzebował, replaceAll("\\*", ".*")gdyby poszli z bardziej delikatną implementacją, która polega na rozpoznawaniu wszystkich możliwych aktywnych charcheterów wyrażeń regularnych i unikaniu ich indywidualnie ... czy to byłoby o wiele łatwiejsze?
Theodore Murdock,
112

Różnica pomiędzy Pattern.quotei Matcher.quoteReplacementnie była dla mnie jasna, zanim zobaczyłem następujący przykład

s.replaceFirst(Pattern.quote("text to replace"), 
               Matcher.quoteReplacement("replacement text"));
Pavel Feldman
źródło
29
W szczególności Pattern.quotezastępuje znaki specjalne w ciągach wyszukiwania wyrażeń regularnych, takich jak. | + () Itp., I Matcher.quoteReplacementzastępuje znaki specjalne w ciągach zastępujących, takich jak \ 1, dla odwołań wstecznych.
Steven
9
Nie zgadzam się Pattern.quote otacza swój argument \ Q i \ E. Nie ucieka od znaków specjalnych.
David Medinets,
5
Matcher.quoteReplacement („4 $ i% $”) produkuje „4 \ $ i% \ $”. Ucieka przed znakami specjalnymi.
David Medinets,
4
Innymi słowy: quoteReplacementdba tylko o dwa symbole $i \ które mogą być na przykład użyte w ciągach zastępczych jako odniesienia wsteczne $1lub \1. Dlatego nie można go używać do ucieczki / cytowania wyrażenia regularnego.
SebastianH
1
Niesamowite. Oto przykład, gdzie chcemy zamienić $Group$z T$UYO$HI. $Symbol jest wyjątkowy zarówno w strukturze oraz w wymianie:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
arun
29

Może być za późno, aby odpowiedzieć, ale możesz również użyć Pattern.LITERAL, który zignoruje wszystkie znaki specjalne podczas formatowania:

Pattern.compile(textToFormat, Pattern.LITERAL);
Androidme
źródło
Jest to szczególnie miłe, ponieważ można go połączyć zPattern.CASE_INSENSITIVE
mjjaniec
13

Myślę, że to, czego szukasz \Q$5\E. Zobacz także Pattern.quote(s)wprowadzone w Java5.

Aby uzyskać szczegółowe informacje, zobacz Wzór javadoc.

Rob Oxspring
źródło
Jestem ciekawy, czy jest jakaś różnica między tym a użyciem flagi LITERAL, ponieważ javadoc mówi, że nie ma wbudowanej flagi do włączania i wyłączania LITERAL
Chris Mazzola,
15
Zauważ, że dosłownie użycie \ Q i \ E jest w porządku, tylko jeśli znasz swój wkład. Pattern.quote (s) zajmie się również przypadkiem, w którym tekst faktycznie zawiera te sekwencje.
Jeremy Huiskamp
10

Po pierwsze, jeśli

  • używasz replaceAll ()
  • NIE korzystasz z Matcher.quoteReplacement ()
  • tekst, który ma zostać zastąpiony, zawiera 1 $

nie umieści 1 na końcu. Spojrzy na wyrażenie regularne wyszukiwania dla pierwszej pasującej grupy i podrzędne TO. To właśnie oznacza 1, 2 lub 3 USD w tekście zastępczym: pasujące grupy ze wzorca wyszukiwania.

Często podłączam długie ciągi tekstu do plików .properties, a następnie generuję z nich tematy i treści wiadomości e-mail. Rzeczywiście wydaje się, że jest to domyślny sposób wykonywania i18n w Spring Framework. Umieszczam tagi XML, jako symbole zastępcze, w ciągach i używam replaceAll (), aby zamienić tagi XML na wartości w czasie wykonywania.

Natknąłem się na problem polegający na tym, że użytkownik wprowadził liczbę dolarów i centów ze znakiem dolara. Zadławiono replaceAll (), a w stracktrace pojawiają się:

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)

W tym przypadku użytkownik wpisał gdzieś „3 $” i replaceAll () zaczął szukać wyrażenia regularnego wyszukiwania dla trzeciej pasującej grupy, nie znalazł jednej i rzygnął.

Dany:

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

zastępując

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

z

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

Rozwiązać problem. Użytkownik może bez problemu wprowadzać dowolne znaki, w tym znaki dolara. Zachowywał się dokładnie tak, jak można się spodziewać.

Meower68
źródło
6

Aby zabezpieczyć wzór, możesz zastąpić wszystkie symbole „\\\\”, z wyjątkiem cyfr i liter. A potem możesz umieścić w tym chronionym wzorze swoje specjalne symbole, aby ten wzór działał nie jak głupi cytowany tekst, ale naprawdę jak wzór, ale twój własny. Bez specjalnych symboli użytkownika.

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");
    }
}
Moscow Boy
źródło
Nie musisz uciekać przed spacjami. Możesz więc zmienić swój wzór na „([^ a-zA-z0-9])”.
Erel Segal-Halevi
5
Mała literówka, duże konsekwencje: „([^ a-zA-z0-9])” również nie pasuje (tzn. Nie ma znaku ucieczki) [, \,], ^, na pewno chcesz uciec! Literówka jest drugim „z”, które powinno być „Z”, w przeciwnym razie wszystko od ASCII 65 do ASCII 122 jest włączone
Zefiro
3

Pattern.quote („blabla”) działa dobrze.

Pattern.quote () działa dobrze. Zawiera zdanie ze znakami „ \ Q ” i „ \ E ”, a jeśli nie ma „\ Q” i „\ E”. Jeśli jednak potrzebujesz wykonać prawdziwe wyrażenie specjalne (lub niestandardowe), możesz użyć tego kodu:

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

Ta metoda zwraca: Niektóre / \ s / wText * / \, **

Kod na przykład i testy:

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

Symbol ^ (Negacja) służy do dopasowania czegoś, co nie znajduje się w grupie znaków.

To jest link do wyrażeń regularnych

Oto informacje o negacji:

Informacje o negacji

Akhil Kathi
źródło