Liste aller Sonderzeichen, die in einem regulären Ausdruck maskiert werden müssen

108

Ich versuche, eine Anwendung zu erstellen, die eine Nachrichtenvorlage mit einer Nachricht vergleicht, die ein Benutzer senden möchte. Ich verwende Java Regex zum Abgleichen der Nachricht. Die Vorlage / Nachricht kann Sonderzeichen enthalten.

Wie würde ich die vollständige Liste der Sonderzeichen erhalten, die maskiert werden müssen, damit meine Regex in den maximal möglichen Fällen funktioniert und übereinstimmt?

Gibt es eine universelle Lösung, um alle Sonderzeichen in Java Regex zu umgehen?

Avinash Nair
quelle

Antworten:

94

Sie können sich das Javadoc der Pattern-Klasse ansehen: http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html

Sie müssen sich allen dort aufgeführten Zeichen entziehen, wenn Sie das reguläre Zeichen und nicht die spezielle Bedeutung möchten.

Als vielleicht einfachere Lösung können Sie die Vorlage zwischen \ Q und \ E platzieren - alles zwischen ihnen wird als maskiert betrachtet.

Sorin
quelle
43
Wenn Sie \ Q und \ E schwer zu merken finden, können Sie stattdessen Pattern.quote ("...")
mkdev
19
Ich wünschte, Sie hätten sie tatsächlich angegeben
Aleksandr Dubinsky
Warum, @AleksandrDubinsky?
Sorin
55
@Sorin Weil es der Geist (nein, die Richtlinie?) Von Stack Exchange ist, die Antwort in Ihrer Antwort anzugeben, anstatt nur auf eine externe Ressource zu verlinken. Außerdem hat diese Seite auch keine klare Liste. Eine Liste finden Sie hier: docs.oracle.com/javase/tutorial/essential/regex/literals.html . Sie besagt jedoch "In bestimmten Situationen werden die oben aufgeführten Sonderzeichen nicht als Metazeichen behandelt", ohne zu erklären, was passieren wird wenn man versucht, ihnen zu entkommen. Kurz gesagt, diese Frage verdient eine gute Antwort.
Aleksandr Dubinsky
8
"Alles zwischen ihnen [ \Qund \E] wird als entkommen betrachtet" - mit Ausnahme anderer \Qund \E(die möglicherweise innerhalb des ursprünglichen regulären Ausdrucks auftreten können). Es ist also besser, Pattern.quotewie hier vorgeschlagen zu verwenden und das Rad nicht neu zu erfinden.
Sasha
90
  • Java-Zeichen, die in regulären Ausdrücken maskiert werden müssen, sind:
    \.[]{}()<>*+-=!?^$|
  • Zwei der schließenden Klammern ( ]und }) müssen erst nach dem Öffnen des gleichen Klammertyps ausgeblendet werden.
  • In []Klammern funktionieren einige Zeichen (wie +und -) manchmal ohne Flucht.
Zu groß.
quelle
Gibt es eine Möglichkeit, nicht zu entkommen, sondern diese Charaktere zuzulassen?
Dominika
1
Ein Zeichen zu entkommen bedeutet, das Zeichen zuzulassen, anstatt es als Operator zu interpretieren.
Tobi G.
4
Unescaped -innerhalb []kann nicht immer funktionieren , da es Bereiche zu definieren , verwendet wird. Es ist sicherer, ihm zu entkommen. Zum Beispiel die Muster [-]und [-)]stimmen mit der Zeichenfolge überein, -aber nicht mit [(-)].
Kenston Choi
1
Obwohl die akzeptierte Antwort die Frage beantwortet, war diese Antwort für mich hilfreicher, als ich nur nach einer kurzen Liste suchte.
Alter Nick
29

Um zu entkommen, können Sie dies einfach aus Java 1.5 verwenden :

Pattern.quote("$test");

Sie werden genau das Wort finden $test

Madx
quelle
Warum ist dies nicht die am höchsten bewertete Antwort? Es löst das Problem, ohne auf die komplexen Details der Auflistung aller Zeichen einzugehen, die maskiert werden müssen, und ist Teil des JDK - Sie müssen keinen zusätzlichen Code schreiben! Einfach!
Volksman
17

Laut der Dokumentationsseite für String-Literale / Metazeichen sind dies:

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

Es wäre auch cool, diese Liste irgendwo im Code zu haben, aber ich weiß nicht, wo das sein könnte ...

Bohdan
quelle
11
String escaped = tnk.replaceAll("[\\<\\(\\[\\{\\\\\\^\\-\\=\\$\\!\\|\\]\\}\\)\\?\\*\\+\\.\\>]", "\\\\$0");
Marbel82
1
Das Muster javadoc sagt es ein Fehler ist , einen umgekehrten Schrägstrich vor jedem alphabetischen Zeichen zu verwenden , die nicht mit Escape - Sequenzen Konstrukt nicht bezeichnen, aber ein Backslash kann , unabhängig davon , auf ein nicht-alphabetisches Zeichen vor verwendet werden , ob das Zeichen Teil eines unescaped Konstrukts ist. Daher wird eine viel einfachere Regex ausreichen: s.replaceAll("[\\W]", "\\\\$0")wobei \WNicht-Wort-Zeichen bezeichnet werden.
Joe Bowbeer
5

Auf @ Sorins Vorschlag der Java Pattern-Dokumente hin sieht es so aus, als wären Zeichen, denen man entkommen muss, mindestens:

\.[{(*+?^$|
pete
quelle
4
String escaped = regexString.replaceAll("([\\\\\\.\\[\\{\\(\\*\\+\\?\\^\\$\\|])", "\\\\$1");
Fracz
2
)muss auch maskiert werden, und je nachdem, ob Sie sich innerhalb oder außerhalb einer Zeichenklasse befinden, müssen möglicherweise mehr Zeichen maskiert werden. In diesem Fall ist Pattern.quotees recht gut, eine Zeichenfolge für die Verwendung innerhalb und außerhalb der Zeichenklasse zu maskieren.
nhahtdh
5

Ich schlage Folgendes vor, um die Liste der für RegExp speziellen Zeichen klar in ihrer eigenen Zeichenfolge aufzulisten und zu vermeiden, dass Tausende von "\\" visuell analysiert werden müssen. Das scheint für mich ziemlich gut zu funktionieren:

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

Die Pattern.quote(String s)Art macht, was Sie wollen. Es lässt jedoch ein wenig zu wünschen übrig; Es entgeht nicht den einzelnen Zeichen, sondern umschließt nur die Zeichenfolge mit \Q...\E.

Es gibt keine Methode, die genau das tut, wonach Sie suchen, aber die gute Nachricht ist, dass es eigentlich ziemlich einfach ist, alle Sonderzeichen in einem regulären Java-Ausdruck zu umgehen:

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

Warum funktioniert das? Nun, die Dokumentation für Patternbesagt ausdrücklich, dass es zulässig ist, nicht alphabetische Zeichen zu maskieren, die nicht unbedingt maskiert werden müssen:

Es ist ein Fehler, vor einem alphabetischen Zeichen, das kein maskiertes Konstrukt kennzeichnet, einen Backslash zu verwenden. Diese sind für zukünftige Erweiterungen der Sprache für reguläre Ausdrücke reserviert. Ein Backslash kann vor einem nicht alphabetischen Zeichen verwendet werden, unabhängig davon, ob dieses Zeichen Teil eines nicht entflohenen Konstrukts ist.

Zum Beispiel ;ist kein Sonderzeichen in einem regulären Ausdruck. Wenn Sie jedoch entkommen, Patternwird immer noch \;als interpretiert ;. Hier noch ein paar Beispiele:

  • >wird \>was äquivalent zu ist>
  • [wird \[was ist die entkommene Form von[
  • 8ist immer noch 8.
  • \)wird \\\)was die entkommenen Formen von \und (verkettet ist.

Hinweis: Der Schlüssel ist die Definition von "nicht alphabetisch", was in der Dokumentation wirklich "Nicht- Wort " -Zeichen oder Zeichen außerhalb des Zeichensatzes bedeutet [a-zA-Z_0-9].

Wheeler
quelle
2

Auf der anderen Seite der Medaille sollten Sie einen Regex ohne Zeichen verwenden, der so aussieht, wenn in Ihrem App-Kontext Sonderzeichen = allChars - Nummer - ABC - Leerzeichen stehen.

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

Die Antwort ist zwar für Java, aber der Code kann leicht von dieser Kotlin-String-Erweiterung angepasst werden, die ich mir ausgedacht habe (angepasst von dem bereitgestellten @ 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())
}

druckt \(\.\*\)

Überprüfen Sie es in Aktion hier https://pl.kotl.in/h-3mXZkNE

Pocesar
quelle
1

Angenommen, Sie haben und vertrauen (um maßgeblich zu sein) der Liste der von Java Regex verwendeten Escape-Zeichen (wäre schön, wenn diese Zeichen in einem Pattern-Klassenmitglied verfügbar gemacht würden), können Sie die folgende Methode verwenden, um das Zeichen zu maskieren, wenn dies tatsächlich erforderlich ist:

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

private static String regexEscape(char character) {
    for (char escapeChar : escapeChars) {
        if (character == escapeChar) {
            return "\\" + character;
        }
    }
    return String.valueOf(character);
}
brcolow
quelle