Pytanie Jak uniknąć tekstu do wyrażenia regularnego w Javie


Czy Java ma wbudowany sposób na uniknięcie dowolnego tekstu, aby można go było włączyć do wyrażenia regularnego? Na przykład, jeśli moi użytkownicy wpiszą "5 USD", chciałbym dopasować to dokładnie zamiast "5" po zakończeniu wprowadzania.


287
2017-09-12 23:36


pochodzenie




Odpowiedzi:


Od Java 1.5, tak:

Pattern.quote("$5");

409
2017-09-12 23:39



Nie oznacza to, że nie wymyka się on samemu napisowi, ale owija go za pomocą \Q i \E. Może to na przykład prowadzić do nieoczekiwanych wyników Pattern.quote("*.wav").replaceAll("*",".*") spowoduje \Q.*.wav\Ei nie .*\.wav, Jak możesz się spodziewać. - Paramaeleon
@Paramaeleon Dlaczego oczekiwałbyś tego foo (x) .bar () == x.bar ()? - Michael
@Paramaeleon Myślę, że źle zrozumiałeś przypadek użycia. - vikingsteve
Chcę tylko zwrócić uwagę, że ten sposób ucieczki odnosi się również do wyrażeń, które wprowadzasz potem. To może być zaskakujące. Jeśli zrobisz "mouse".toUpperCase().replaceAll("OUS","ic") to powróci MicE. Nie spodziewałbyś się, że powróci MICE ponieważ nie złożyłeś podania toUpperCase() na ic. W moim przykładzie quote() jest stosowany na .* wstawić przez replaceAll() także. Być może musisz zrobić coś innego .replaceAll("*","\\E.*\\Q") zadziała, ale jest to sprzeczne z intuicją. - Paramaeleon
@Paramaleon Jeśli to działało przez dodawanie indywidualnych ucieczek, twój początkowy przykład nadal nie zrobiłby tego, co chciałeś ... gdyby uniknął znaków pojedynczo, *.wav w wzór regex \*\.wav, a replaceAll go zamieni \.*\.wav, co oznacza, że ​​pasowałoby do plików, których nazwa składa się z dowolnej liczby kropek .wav. Najprawdopodobniej musiałbyś replaceAll("\\*", ".*") gdyby poszli z delikatniejszą implementacją polegającą na rozpoznawaniu wszystkich możliwych aktywnych symboli regex i unikaniu ich pojedynczo ... byłoby to o wiele łatwiejsze? - Theodore Murdock


Różnica pomiędzy Pattern.quote i Matcher.quoteReplacement nie było dla mnie jasne, zanim zobaczyłem następujący przykład

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

96
2017-09-12 23:52



Konkretnie, Pattern.quote zastępuje znaki specjalne w ciągach wyszukiwania regex, takich jak. | + () itd. oraz Matcher.quoteReplacement zastępuje znaki specjalne w łańcuchach zastępczych, takich jak \ 1 dla odsyłaczy wstecz. - Steven
Nie zgadzam się. Pattern.quote opakowuje swój argument za pomocą \ Q i \ E. Nie ucieka od znaków specjalnych. - David Medinets
Matcher.quoteReplacement ("4 $ &% $") generuje "4 \ $ &% \ $". Unika znaków specjalnych. - David Medinets
Innymi słowy: quoteReplacement tylko dba o te dwa symbole $ i \  który może na przykład być używany w łańcuchach zastępczych jako backreferencje $1 lub \1. Dlatego nie należy go używać do ucieczki / zacytowania wyrażenia regularnego. - SebastianH
Niesamowite. Oto przykład, w którym chcemy zastąpić $Group$ z T$UYO$HI. The $ Symbol jest szczególny zarówno we wzorze, jak i w zastępstwie: "$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI")) - arun


Może być za późno na odpowiedź, ale możesz też użyć Pattern.LITERAL, które zignoruje wszystkie znaki specjalne podczas formatowania:

Pattern.compile(textToFormat, Pattern.LITERAL);

24
2018-03-14 15:04



Jest to szczególnie miłe, ponieważ można z nim łączyć Pattern.CASE_INSENSITIVE - mjjaniec


Myślę, że to, o co prosisz, jest \Q$5\E. Zobacz także Pattern.quote(s) wprowadzone w Javie5.

Widzieć Wzór javadoc po szczegóły.


13
2017-09-12 23:42



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 LITERA: java.sun.com/j2se/1.5.0/docs/api/java/util/regex/... - Chris Mazzola
Zauważ, że dosłowne użycie \ Q i \ E jest w porządku, tylko jeśli znasz swoje dane wejściowe. Pattern.quote (s) zajmie się również przypadkiem, w którym twój tekst rzeczywiście zawiera te sekwencje. - Jeremy Huiskamp


Po pierwsze, jeśli

  • używasz replaceAll ()
  • NIE używasz Matcher.quoteReplacement ()
  • tekst do zastąpienia zawiera 1 $

nie umieści 1 na końcu. Spojrzymy na wyliczenie wyszukiwania dla pierwszej pasującej grupy i poniżej THAT. To oznacza, że ​​1 $, 2 $ lub 3 $ oznacza w tekście zastępującym: dopasowywanie grup do wzorca wyszukiwania.

Często podłączam długie ciągi tekstu do plików .properties, a następnie generuję tematy i ciała e-maili z nich. Rzeczywiście, wydaje się, że jest to domyślny sposób tworzenia i18n w Spring Framework. Umieściłem znaczniki XML jako symbole zastępcze w łańcuchach i używam replaceAll (), aby zastąpić znaczniki XML wartościami w czasie wykonywania.

Wpadłem na problem polegający na tym, że użytkownik wprowadził cyfrę w dolarach i dolara. replaceAll () zakrztusił się na nim, a na ścieżce strackowej pojawiły 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 wprowadził "$ 3" gdzieś w swoich danych wejściowych, a replaceAll () szukał w regexie wyszukiwania trzeciej pasującej grupy, nie znalazł ani nie pukował.

Dany:

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

zastępowanie

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

z

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

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


10
2017-08-14 15:00





Aby mieć chroniony wzorzec, możesz zamienić wszystkie symbole na "\\\\", 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 patten, 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");
    }
}

4
2017-11-15 20:27



Nie musisz uciekać spacji. Możesz więc zmienić swój wzór na "([^ a-zA-z0-9])". - Erel Segal-Halevi
Mała literówka, duże konsekwencje: "([^ a-zA-z0-9])" również nie pasuje (tzn. Nie ucieka) [, \,], ^ które z pewnością chcesz uciec! Literówka jest drugim "z", które powinno być literą "Z", w przeciwnym razie zawiera się wszystko od ASCII 65 do ASCII 122 - Zefiro


Pattern.quote ("blabla") działa ładnie.

Funkcja Pattern.quote () działa ładnie. Zawiera zdanie ze znakami "\ Q" i "\MI", a jeśli ucieka" \ Q "i" \ E ". Jeśli jednak chcesz wykonać rzeczywiste wyrazy regularne (lub niestandardowe znaki ucieczki), możesz użyć tego kodu:

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

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

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

1
2017-07-31 12:26





Symbol ^ (Negacja) służy do dopasowania czegoś, czego nie ma w grupie znaków.
Informacje o negacji

Wyrażenia regularne


0
2018-06-22 15:00