Anleitung zum Entweichen von Zeichen in Java RegExps

Leitfaden zum Entkommen von Zeichen in Java RegExps

1. Überblick

Die API für reguläre Ausdrücke in Java,java.util.regex, wird häufig für den Mustervergleich verwendet. Um mehr zu erfahren, können Sie diesenarticle folgen.

In diesem Artikel konzentrieren wir uns darauf, Zeichen innerhalb eines regulären Ausdrucks zu maskieren und zu zeigen, wie dies in Java geschehen kann.

2. Spezielle RegExp-Zeichen

Gemäß der API-Dokumentation zu regulären Java-Ausdrücken gibt es eine Reihe von Sonderzeichen, die auch als Metazeichen in regulären Ausdrücken bezeichnet werden.

Wenn wir den Zeichen erlauben wollen, wie sie sind, anstatt sie mit ihren speziellen Bedeutungen zu interpretieren, müssen wir ihnen entkommen. Indem wir diese Zeichen maskieren, zwingen wir sie, als gewöhnliche Zeichen behandelt zu werden, wenn eine Zeichenfolge mit einem bestimmten regulären Ausdruck abgeglichen wird.

Die Metazeichen, die wir normalerweise brauchen, um auf diese Weise zu entkommen, sind:

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

Schauen wir uns ein einfaches Codebeispiel an, in dem wir eine EingabeString mit einem Muster abgleichen, das in einem regulären Ausdruck ausgedrückt wird.

Dieser Test zeigt, dass für eine gegebene Eingabezeichenfolgefoof das Musterfoo ist. (foo endet mit einem Punktzeichen) wird abgeglichen. Es wird ein Wert vontrue zurückgegeben, der angibt, dass die Übereinstimmung erfolgreich ist.

@Test
public void givenRegexWithDot_whenMatchingStr_thenMatches() {
    String strInput = "foof";
    String strRegex = "foo.";

    assertEquals(true, strInput.matches(strRegex));
}

Sie fragen sich vielleicht, warum die Übereinstimmung erfolgreich ist, wenn in der EingabeString? kein Punkt (.) Enthalten ist

Die Antwort ist einfach. Der Punkt (.) Ist ein Metazeichen - die besondere Bedeutung des Punkts besteht darin, dass an seiner Stelle „jedes Zeichen“ stehen kann. Daher ist klar, wie der Matcher festgestellt hat, dass eine Übereinstimmung gefunden wurde.

Nehmen wir an, wir möchten das Punktzeichen (.) Nicht mit seiner eindeutigen Bedeutung behandeln. Stattdessen möchten wir, dass es als Punktzeichen interpretiert wird. Dies bedeutet, dass wir im vorherigen Beispiel nicht möchten, dass das Musterfoo. mit der EingabeString. übereinstimmt

Wie würden wir mit einer solchen Situation umgehen? Die Antwort lautet:we need to escape the dot (.) character so that its special meaning gets ignored.

Lassen Sie uns im nächsten Abschnitt näher darauf eingehen.

3. Fluchtzeichen

Gemäß der Java-API-Dokumentation für reguläre Ausdrücke gibt es zwei Möglichkeiten, Zeichen mit besonderer Bedeutung zu umgehen. Mit anderen Worten, um sie zu zwingen, wie gewöhnliche Charaktere behandelt zu werden.

Mal sehen, was sie sind:

  1. Stellen Sie einem Metazeichen einen Backslash (\) voran

  2. Schließen Sie ein Metazeichen mit\Q und\E ein

Dies bedeutet nur, dass in dem Beispiel, das wir zuvor gesehen haben, ein Backslash-Zeichen vor das Punkt-Zeichen gesetzt werden muss, wenn das Punkt-Zeichen ausgeblendet werden soll. Alternativ können wir das Punktzeichen zwischen \ Q und \ E setzen.

3.1. Escape mit Backslash

Dies ist eine der Techniken, die wir verwenden können, um Metazeichen in einem regulären Ausdruck zu maskieren. Wir wissen jedoch, dass das Backslash-Zeichen auch in JavaString-Literalen ein Escape-Zeichen ist. Daher müssen wir den umgekehrten Schrägstrich verdoppeln, wenn wir ihn vor einem beliebigen Zeichen verwenden (einschließlich des Zeichens \).

Daher müssen wir in unserem Beispiel den regulären Ausdruck wie folgt ändern:

@Test
public void givenRegexWithDotEsc_whenMatchingStr_thenNotMatching() {
    String strInput = "foof";
    String strRegex = "foo\\.";

    assertEquals(false, strInput.matches(strRegex));
}

Hier wird das Punktzeichen ausgeblendet, sodass der Matcher es einfach als Punkt behandelt und versucht, ein Muster zu finden, das mit dem Punkt endet (d. H. foo.).

In diesem Fall wirdfalse zurückgegeben, da die EingabeString für dieses Muster nicht übereinstimmt.

3.2. Escaping mit \ Q & \ E.

Alternativ können wir\Q und\E verwenden, um dem Sonderzeichen zu entkommen. \Q gibt an, dass alle Zeichen bis zu\E maskiert werden müssen, und\E bedeutet, dass die mit\Q begonnene Escapezeichen beendet werden müssen.

Dies bedeutet nur, dass alles, was zwischen\Q und\E liegt, maskiert wird.

In dem hier gezeigten Test führtsplit() derString-Klasse eine Übereinstimmung unter Verwendung des ihm bereitgestellten regulären Ausdrucks durch.

Unsere Anforderung besteht darin, die Eingabezeichenfolge durch das Pipe-Zeichen (|) in Wörter zu teilen. Aus diesem Grund verwenden wir ein reguläres Ausdrucksmuster.

Das Pipe-Zeichen ist ein Metazeichen, das im regulären Ausdruck maskiert werden muss.

Hier erfolgt die Flucht, indem das Pipe-Zeichen zwischen\Q und\E gesetzt wird:

@Test
public void givenRegexWithPipeEscaped_whenSplitStr_thenSplits() {
    String strInput = "foo|bar|hello|world";
    String strRegex = "\\Q|\\E";

    assertEquals(4, strInput.split(strRegex).length);
}

4. Die Pattern.Quote (String S) -Methode

Die Pattern.Quote (String S) -Methode in der Klassejava.util.regex.Pattern konvertiert ein bestimmtes Muster für reguläre AusdrückeString in ein LiteralmusterString.. Dies bedeutet, dass alle Metazeichen in der EingabeString sind als gewöhnliche Zeichen behandelt.

Die Verwendung dieser Methode wäre eine bequemere Alternative als die Verwendung von\Q &\E, da sie die angegebenenString mit ihnen umschließt.

Lassen Sie uns diese Methode in Aktion sehen:

@Test
public void givenRegexWithPipeEscQuoteMeth_whenSplitStr_thenSplits() {
    String strInput = "foo|bar|hello|world";
    String strRegex = "|";

    assertEquals(4,strInput.split(Pattern.quote(strRegex)).length);
}

In diesem Schnelltest wird diePattern.quote()-Methode verwendet, um dem angegebenen Regex-Muster zu entkommen und es in einString-Literal umzuwandeln. Mit anderen Worten, es entgeht uns allen im Regex-Muster vorhandenen Metazeichen. Es macht einen ähnlichen Job wie\Q &\E.

Das Pipe-Zeichen wird durch diePattern.quote()-Methode maskiert und vonsplit() alsString-Literal interpretiert, durch das die Eingabe geteilt wird.

Wie wir sehen können, ist dies ein viel saubererer Ansatz, und auch die Entwickler müssen sich nicht alle Escape-Sequenzen merken.

5. Zusätzliche Beispiele

Schauen wir uns an, wie diereplaceAll()-Methode vonjava.util.regex.Matcherfunktioniert.

Wenn wir alle Vorkommen eines bestimmten ZeichensString durch ein anderes ersetzen müssen, können wir diese Methode verwenden, indem wir einen regulären Ausdruck an sie übergeben.

Stellen Sie sich vor, wir haben eine Eingabe mit mehreren Vorkommen des Zeichens$. Das Ergebnis, das wir erhalten möchten, ist dieselbe Zeichenfolge, bei der das Zeichen$durch £ ersetzt wird.

Dieser Test zeigt, wie das Muster$ übergeben wird, ohne dass es maskiert wird:

@Test
public void givenRegexWithDollar_whenReplacing_thenNotReplace() {

    String strInput = "I gave $50 to my brother."
      + "He bought candy for $35. Now he has $15 left.";
    String strRegex = "$";
    String strReplacement = "£";
    String output = "I gave £50 to my brother."
      + "He bought candy for £35. Now he has £15 left.";

    Pattern p = Pattern.compile(strRegex);
    Matcher m = p.matcher(strInput);

    assertThat(output, not(equalTo(m.replaceAll(strReplacement))));
}

Der Test bestätigt, dass$ nicht korrekt durch£ ersetzt wird.

Wenn wir nun das reguläre Ausdrucksmuster verlassen, geschieht das Ersetzen korrekt und der Test wird wie in diesem Codeausschnitt gezeigt bestanden:

@Test
public void givenRegexWithDollarEsc_whenReplacing_thenReplace() {

    String strInput = "I gave $50 to my brother."
      + "He bought candy for $35. Now he has $15 left.";
    String strRegex = "\\$";
    String strReplacement = "£";
    String output = "I gave £50 to my brother."
      + "He bought candy for £35. Now he has £15 left.";
    Pattern p = Pattern.compile(strRegex);
    Matcher m = p.matcher(strInput);

    assertEquals(output,m.replaceAll(strReplacement));
}

Beachten Sie hier die\$, die den Trick ausführen, indem sie dem Zeichen$entkommen und das Muster erfolgreich abgleichen.

6. Fazit

In diesem Artikel haben wir uns mit Escapezeichen in regulären Ausdrücken in Java befasst.

Wir diskutierten, warum reguläre Ausdrücke maskiert werden müssen und auf welche Weise dies erreicht werden kann.

Wie immer befindet sich der Quellcode zu diesem Artikel inover on GitHub.