Lista wszystkich znaków specjalnych, które muszą zostać zmienione w wyrażeniu regularnym


108

Próbuję utworzyć aplikację, która dopasowuje szablon wiadomości do wiadomości, którą użytkownik próbuje wysłać. Do dopasowania wiadomości używam wyrażenia regularnego Java. Szablon / wiadomość może zawierać znaki specjalne.

Jak uzyskać pełną listę znaków specjalnych, które muszą zostać zmienione, aby moje wyrażenie regularne działało i pasowało w maksymalnych możliwych przypadkach?

Czy istnieje uniwersalne rozwiązanie umożliwiające unikanie wszystkich znaków specjalnych w wyrażeniu regularnym Java?

Odpowiedzi:


94

Możesz spojrzeć na javadoc klasy Pattern: http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

Jeśli chcesz użyć zwykłego znaku, a nie specjalnego znaczenia, musisz uciec przed każdym znakiem wymienionym tam.

Być może prostszym rozwiązaniem jest umieszczenie szablonu między \ Q i \ E - wszystko między nimi jest traktowane jako uciekające.


43
Jeśli uważasz, że \ Q i \ E są trudne do zapamiętania, możesz użyć zamiast tego Pattern.quote ("...")
mkdev

19
Chciałbym, żebyś je faktycznie określił
Aleksandr Dubinsky

Dlaczego @AleksandrDubinsky?
Sorin

55
@Sorin Ponieważ duch (nie, polityka?) Stack Exchange polega na podawaniu odpowiedzi w Twojej odpowiedzi, a nie tylko na linkowaniu do zasobów zewnętrznych. Poza tym ta strona też nie ma przejrzystej listy. Listę można znaleźć tutaj: docs.oracle.com/javase/tutorial/essential/regex/literals.html , ale zawiera ona informację „W niektórych sytuacjach znaki specjalne wymienione powyżej nie będą traktowane jako metaznaki” bez wyjaśnienia, co się stanie jeśli ktoś próbuje przed nimi uciec. Krótko mówiąc, to pytanie zasługuje na dobrą odpowiedź.
Aleksandr Dubinsky

8
„wszystko pomiędzy nimi [ \Qi \E] jest uważane za uciekające” - z wyjątkiem innych \Q'i \E' (które potencjalnie mogą wystąpić w oryginalnym wyrażeniu regularnym). Dlatego lepiej jest użyć tego, Pattern.quoteco sugerowano tutaj i nie wymyślać koła na nowo.
Sasha

92
  • Znaki Java, które muszą zostać zmienione w wyrażeniach regularnych to:
    \.[]{}()<>*+-=!?^$|
  • Dwa z nawiasów zamykających ( ]i }) należy usunąć dopiero po otwarciu wspornika tego samego typu.
  • W []nawiasach niektóre znaki (takie jak +i -) czasami działają bez ucieczki.

Czy jest jakiś sposób, aby nie uciec, ale pozwolić tym postaciom?
Dominika

1
Unikanie znaku oznacza zezwolenie na znak zamiast interpretowania go jako operatora.
Tobi G.

4
Bez zmiany znaczenia -w obrębie []może nie zawsze działać, ponieważ jest używany do definiowania zakresów. Bezpieczniej jest od tego uciec. Na przykład wzorce [-]i [-)]pasują do ciągu, -ale nie do [(-)].
Kenston Choi

1
Mimo że zaakceptowana odpowiedź odpowiada na pytanie, ta odpowiedź była dla mnie bardziej pomocna, gdy szukałem tylko krótkiej listy.
Old Nick

-=!niekoniecznie trzeba uciekać, to zależy od kontekstu. Na przykład jako pojedyncza litera działają jako stałe wyrażenie regularne.
Hawk

29

Aby uciec, możesz po prostu użyć tego z Java 1.5 :

Pattern.quote("$test");

Dopasujesz dokładnie to słowo $test


Dlaczego nie jest to najwyżej oceniana odpowiedź? Rozwiązuje problem bez wchodzenia w skomplikowane szczegóły listy wszystkich znaków, które wymagają ucieczki i jest częścią JDK - nie ma potrzeby pisania dodatkowego kodu! Prosty!
Volksman

17

Według strony dokumentacji String Literals / Metaznaki są to:

<([{\^-=$!|]})?*+.>

Byłoby też fajnie, gdyby ta lista była gdzieś w kodzie, ale nie wiem, gdzie to może być ...


11
String escaped = tnk.replaceAll("[\\<\\(\\[\\{\\\\\\^\\-\\=\\$\\!\\|\\]\\}\\)\\?\\*\\+\\.\\>]", "\\\\$0");
marbel82

1
Wzorzec javadoc mówi, że błędem jest użycie ukośnika odwrotnego przed jakimkolwiek znakiem alfabetycznym, który nie oznacza konstrukcji z ucieczką, ale ukośnik odwrotny może być użyty przed znakiem niealfabetycznym, niezależnie od tego, czy ten znak jest częścią konstrukcji bez znaku zmiany znaczenia. Dlatego wystarczy znacznie prostsze wyrażenie regularne: s.replaceAll("[\\W]", "\\\\$0")gdzie \Woznacza znaki niebędące słowami.
Joe Bowbeer

6

Łącząc to, co wszyscy mówili, proponuję co następuje, aby lista znaków specjalnych RegExp była wyraźnie wymieniona w ich własnym łańcuchu znaków i aby uniknąć konieczności wizualnego analizowania tysięcy znaków „\\”. Wydaje mi się, że działa to całkiem nieźle:

final String regExSpecialChars = "<([{\\^-=$!|]})?*+.>";
final String regExSpecialCharsRE = regExSpecialChars.replaceAll( ".", "\\\\$0");
final Pattern reCharsREP = Pattern.compile( "[" + regExSpecialCharsRE + "]");

String quoteRegExSpecialChars( String s)
{
    Matcher m = reCharsREP.matcher( s);
    return m.replaceAll( "\\\\$0");
}

5

Zgodnie z sugestią @ Sorin dotyczącą dokumentacji Java Pattern, wygląda na to, że znaki, które należy uciec, to co najmniej:

\.[{(*+?^$|

4
String escaped = regexString.replaceAll("([\\\\\\.\\[\\{\\(\\*\\+\\?\\^\\$\\|])", "\\\\$1");
fracz

2
)również musi zostać zmieniony, aw zależności od tego, czy jesteś w klasie znaków, czy poza nią, może być więcej znaków do ucieczki, w takim przypadku Pattern.quotecałkiem dobrze radzi sobie z ucieczką z ciągu do użycia zarówno wewnątrz, jak i na zewnątrz klasy znaków.
nhahtdh

3

Pattern.quote(String s)Rodzaju robi to, co chcesz. Jednak pozostawia trochę do życzenia; w rzeczywistości nie ucieka przed pojedynczymi znakami, po prostu zawija ciąg znaków \Q...\E.

Nie ma metody, która robi dokładnie to, czego szukasz, ale dobrą wiadomością jest to, że w rzeczywistości dość łatwo jest uciec przed wszystkimi znakami specjalnymi w wyrażeniu regularnym Java:

regex.replaceAll("[\\W]", "\\\\$0")

Dlaczego to działa? Cóż, dokumentacja Patternkonkretnie mówi, że dozwolone jest unikanie znaków niealfabetycznych, które niekoniecznie muszą być chronione:

Błędem jest użycie odwrotnego ukośnika przed jakimkolwiek znakiem alfabetycznym, który nie oznacza konstrukcji ze zmienioną wartością; są one zarezerwowane dla przyszłych rozszerzeń języka wyrażeń regularnych. Odwrotnego ukośnika można użyć przed znakiem niealfabetycznym, niezależnie od tego, czy ten znak jest częścią konstrukcji bez znaku zmiany znaczenia.

Na przykład ;nie jest znakiem specjalnym w wyrażeniu regularnym. Jeśli jednak uciec od tego, Patternnadal będzie interpretować \;jako ;. Oto kilka innych przykładów:

  • >staje się \>co jest równoważne z>
  • [staje \[się formą ucieczki[
  • 8jest nadal 8.
  • \)staje się tym, \\\)co uciekło \i zostało (połączone.

Uwaga: Kluczem jest definicja „niealfabetyczna”, co w dokumentacji tak naprawdę oznacza znaki „inne niż słowo ” lub znaki spoza zestawu znaków [a-zA-Z_0-9].


2

po drugiej stronie monety powinieneś użyć wyrażenia regularnego „non-char”, które wygląda tak, jeśli znaki specjalne = allChars - number - ABC - space w kontekście twojej aplikacji.

String regepx = "[^\\s\\w]*";

2

chociaż odpowiedź dotyczy Javy, ale kod można łatwo zaadaptować z tego rozszerzenia Kotlin String, które wymyśliłem (zaadaptowano z dostarczonego @brcolow):

private val escapeChars = charArrayOf(
    '<',
    '(',
    '[',
    '{',
    '\\',
    '^',
    '-',
    '=',
    '$',
    '!',
    '|',
    ']',
    '}',
    ')',
    '?',
    '*',
    '+',
    '.',
    '>'
)

fun String.escapePattern(): String {
    return this.fold("") {
      acc, chr ->
        acc + if (escapeChars.contains(chr)) "\\$chr" else "$chr"
    }
}

fun main() {
    println("(.*)".escapePattern())
}

wydruki \(\.\*\)

sprawdź to w akcji tutaj https://pl.kotl.in/h-3mXZkNE


1

Zakładając, że masz i ufasz (aby być autorytatywnym) listą znaków zmiany znaczenia używanych w wyrażeniach regularnych Java (byłoby miło, gdyby te znaki zostały ujawnione w jakimś elemencie klasy Pattern), możesz użyć następującej metody, aby uniknąć znaku, jeśli jest to rzeczywiście konieczne:

private static final char[] escapeChars = { '<', '(', '[', '{', '\\', '^', '-', '=', '$', '!', '|', ']', '}', ')', '?', '*', '+', '.', '>' };

private static String regexEscape(char character) {
    for (char escapeChar : escapeChars) {
        if (character == escapeChar) {
            return "\\" + character;
        }
    }
    return String.valueOf(character);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.