So maskieren Sie Text für reguläre Ausdrücke in Java


320

Verfügt Java über eine integrierte Methode, um beliebigen Text zu umgehen, damit er in einen regulären Ausdruck aufgenommen werden kann? Wenn meine Benutzer beispielsweise "$ 5" eingeben, möchte ich genau das und nicht eine "5" nach dem Ende der Eingabe abgleichen.

Antworten:


450

Seit Java 1.5 ja :

Pattern.quote("$5");

88
Bitte beachten Sie, dass dies nicht dem String selbst entgeht, sondern ihn mit \Qund umschließt \E. Dies kann zu unerwarteten Ergebnissen führen, die beispielsweise Pattern.quote("*.wav").replaceAll("*",".*")zu \Q.*.wav\Eund nicht zu den erwarteten Ergebnissen führen .*\.wav.
Matthias Ronge

11
@Paramaeleon Warum würdest du das foo (x) .bar () == x.bar () erwarten?
Michael

7
@Paramaeleon Ich denke, Sie verstehen den Anwendungsfall falsch.
Wikingersteve

18
Ich möchte nur darauf hinweisen, dass diese Art der Flucht auch für Ausdrücke gilt, die Sie später einführen . Dies kann überraschend sein. Wenn Sie dies tun "mouse".toUpperCase().replaceAll("OUS","ic"), wird es zurückkehren MicE. Sie könnte nicht erwarten , dass es zurück , MICEweil Sie nicht anwenden toUpperCase()auf ic. In meinem Beispiel quote()wird das .*Insertet auch von angewendet replaceAll(). Sie müssen etwas anderes tun, .replaceAll("*","\\E.*\\Q")würde vielleicht funktionieren, aber das ist nicht intuitiv.
Matthias Ronge

2
@Paramaleon Wenn es durch Hinzufügen einzelner Escapezeichen funktioniert hätte, würde Ihr erstes Beispiel immer noch nicht das tun, was Sie wollten ... Wenn es Zeichen einzeln maskieren würde , würde es sich *.wavin das Regex-Muster verwandeln \*\.wav, und das replaceAll würde es in verwandeln \.*\.wav, was bedeutet, dass dies der Fall wäre Übereinstimmungsdateien, deren Name aus einer beliebigen Anzahl von Punkten besteht, gefolgt von .wav. Sie hätten es höchstwahrscheinlich nötig gehabt, replaceAll("\\*", ".*")wenn sie sich für die fragilere Implementierung entschieden hätten, die darauf beruht, alle möglichen aktiven Regex-Zeichen zu erkennen und sie einzeln zu umgehen ... wäre das so viel einfacher?
Theodore Murdock

112

Der Unterschied zwischen Pattern.quoteund Matcher.quoteReplacementwar mir nicht klar, bevor ich das folgende Beispiel sah

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

29
Ersetzt Pattern.quoteinsbesondere Sonderzeichen in Regex-Suchzeichenfolgen wie. | + () Matcher.quoteReplacementUsw. und ersetzt Sonderzeichen in Ersatzzeichenfolgen wie \ 1 für Rückreferenzen.
Steven

9
Ich stimme nicht zu Pattern.quote schließt sein Argument mit \ Q und \ E ab. Sonderzeichen entgehen nicht.
David Medinets

5
Matcher.quoteReplacement ("4 $ &% $") erzeugt "4 \ $ &% \ $". Es entgeht den Sonderzeichen.
David Medinets

4
Mit anderen Worten: quoteReplacementnur Sorgen um die beiden Symbole $und \ welche beispielsweise in Ersatzzeichenfolgen als Rückreferenzierungen verwendet werden $1oder \1. Es darf daher nicht verwendet werden, um einem regulären Ausdruck zu entkommen / ihn zu zitieren.
SebastianH

1
Genial. Hier ist ein Beispiel , wo wir ersetzen möchten $Group$mit T$UYO$HI. Das $Symbol ist sowohl im Muster als auch im Ersatz besonders:"$Group$ Members".replaceFirst(Pattern.quote("$Group$"), Matcher.quoteReplacement("T$UYO$HI"))
Arun

29

Es kann zu spät sein, um zu antworten, aber Sie können auch Folgendes verwenden Pattern.LITERAL, bei dem alle Sonderzeichen beim Formatieren ignoriert werden:

Pattern.compile(textToFormat, Pattern.LITERAL);

Es ist besonders schön, weil Sie es mitPattern.CASE_INSENSITIVE
mjjaniec

13

Ich denke, was Sie suchen, ist \Q$5\E. Siehe Pattern.quote(s)auch in Java5 eingeführt.

Weitere Informationen finden Sie unter Muster- Javadoc.


Ich bin gespannt, ob es einen Unterschied zwischen dieser und der Verwendung des LITERAL-Flags gibt, da der Javadoc angibt, dass es kein eingebettetes Flag zum Ein- und Ausschalten von LITERAL gibt
Chris Mazzola

15
Beachten Sie, dass die Verwendung von \ Q und \ E nur dann in Ordnung ist, wenn Sie Ihre Eingabe kennen. Pattern.quote (s) behandelt auch den Fall, in dem Ihr Text diese Sequenzen tatsächlich enthält.
Jeremy Huiskamp

10

Zunächst einmal, wenn

  • Sie verwenden replaceAll ()
  • Sie verwenden Matcher.quoteReplacement () NICHT
  • Der zu ersetzende Text enthält 1 US-Dollar

Am Ende wird keine 1 gesetzt. Es wird der Such-Regex für die erste übereinstimmende Gruppe und das Unter-DAS angezeigt. Das bedeutet 1 $, 2 $ oder 3 $ im Ersatztext: Übereinstimmende Gruppen aus dem Suchmuster.

Ich füge häufig lange Textzeichenfolgen in .properties-Dateien ein und generiere dann aus diesen E-Mail-Betreffs und -Körper. In der Tat scheint dies die Standardmethode für i18n in Spring Framework zu sein. Ich füge XML-Tags als Platzhalter in die Zeichenfolgen ein und verwende replaceAll (), um die XML-Tags zur Laufzeit durch die Werte zu ersetzen.

Ich bin auf ein Problem gestoßen, bei dem ein Benutzer eine Dollar-und-Cent-Zahl mit einem Dollarzeichen eingegeben hat. replaceAll () ist daran erstickt, wobei Folgendes in einer Spur angezeigt wird:

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)

In diesem Fall hatte der Benutzer irgendwo in seiner Eingabe "$ 3" eingegeben und replaceAll () suchte im Suchbegriff nach der dritten übereinstimmenden Gruppe, fand keine und kotzte.

Gegeben:

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

ersetzen

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

mit

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

Problem gelöst. Der Benutzer kann ohne Probleme jede Art von Zeichen, einschließlich Dollarzeichen, eingeben. Es hat sich genau so verhalten, wie Sie es erwarten würden.


6

Um ein geschütztes Muster zu erhalten, können Sie alle Symbole mit Ausnahme von Ziffern und Buchstaben durch "\\\\" ersetzen. Und danach können Sie in dieses geschützte Muster Ihre speziellen Symbole einfügen, damit dieses Muster nicht wie dumm zitierter Text funktioniert, sondern wirklich wie ein Muster, sondern wie Ihr eigenes. Ohne benutzerspezifische Symbole.

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

Sie müssen keinen Leerzeichen entkommen. So können Sie Ihr Muster auf "([^ a-zA-z0-9])" ändern.
Erel Segal-Halevi

5
Kleiner Tippfehler, große Konsequenzen: "([^ a-zA-z0-9])" stimmt auch nicht überein (dh nicht entkommen) [, \,], ^ dem Sie sicherlich entkommen wollen! Der Tippfehler ist das zweite 'z', das ein 'Z' sein sollte, sonst ist alles von ASCII 65 bis ASCII 122 enthalten
Zefiro

3

Pattern.quote ("blabla") funktioniert gut.

Das Pattern.quote () funktioniert gut. Es schließt den Satz mit den Zeichen " \ Q " und " \ E " ein, und wenn er nicht "\ Q" und "\ E" enthält. Wenn Sie jedoch einen echten Escape-Ausdruck für reguläre Ausdrücke (oder einen benutzerdefinierten Escape-Vorgang) ausführen müssen, können Sie diesen Code verwenden:

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

Diese Methode gibt Folgendes zurück: Some / \ s / wText * / \, **

Code zum Beispiel und Tests:

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

-2

^ Das Symbol (Negation) wird verwendet, um etwas zu finden, das nicht zur Zeichengruppe gehört.

Dies ist der Link zu regulären Ausdrücken

Hier sind die Bildinformationen zur Negation:

Infos zur Verneinung

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.