Reguläre Ausdrücke in Java, Teil 1: Pattern Matching und die Pattern-Klasse
Javas Zeichen- und verschiedene Zeichenfolgenklassen bieten Unterstützung auf niedriger Ebene für den Mustervergleich, aber diese Unterstützung führt normalerweise zu komplexem Code. Für eine einfachere und effizientere Codierung bietet Java die Regex-API an. Dieses zweiteilige Tutorial hilft Ihnen beim Einstieg in reguläre Ausdrücke und die Regex-API. Zuerst entpacken wir die drei leistungsstarken Klassen im java.util.regex
Paket, dann untersuchen wir die Pattern
Klasse und ihre ausgeklügelten Mustervergleichskonstrukte.
Was sind reguläre Ausdrücke?
Ein regulärer Ausdruck , auch als Regex oder Regexp bezeichnet , ist eine Zeichenfolge, deren Muster (Vorlage) eine Reihe von Zeichenfolgen beschreibt. Das Muster bestimmt, welche Zeichenfolgen zum Satz gehören. Ein Muster besteht aus wörtlichen Zeichen und Metazeichen , bei denen es sich um Zeichen handelt, die anstelle einer wörtlichen Bedeutung eine besondere Bedeutung haben.
Beim Mustervergleich wird nach Text gesucht, um Übereinstimmungen oder Zeichenfolgen zu identifizieren , die mit dem Muster eines regulären Ausdrucks übereinstimmen. Java unterstützt den Mustervergleich über seine Regex-API. Die API besteht aus drei Klassen Pattern
- ,, Matcher
und PatternSyntaxException
-, die sich alle im java.util.regex
Paket befinden:
Pattern
Objekte, auch Muster genannt , sind regulierte Regexe.Matcher
Objekte oder Matcher sind Engines, die Muster interpretieren, um Übereinstimmungen in Zeichenfolgen zu finden (Objekte, deren Klassen diejava.lang.CharSequence
Schnittstelle implementieren und als Textquellen dienen).PatternSyntaxException
Objekte beschreiben illegale Regex-Muster.
Java bietet auch Unterstützung für den Mustervergleich über verschiedene Methoden in seiner java.lang.String
Klasse. Gibt beispielsweise nur dannboolean matches(String regex)
true zurück, wenn die aufrufende Zeichenfolge genau mit regex
dem regulären Ausdruck übereinstimmt .
Convenience-Methoden
Hinter den Kulissen, matches()
und String
‚s andere Regex orientierten Komfortmethoden sind im Hinblick auf die Regex API implementiert.
RegexDemo
Ich habe das erstellte RegexDemo
Anwendung Java reguläre Ausdrücke und die verschiedenen Methoden in der Lage zu zeigen Pattern
, Matcher
und PatternSyntaxException
Klassen. Hier ist der Quellcode für die Demo:
Listing 1. Demonstration von regulären Ausdrücken
import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; public class RegexDemo { public static void main(String[] args) { if (args.length != 2) { System.err.println("usage: java RegexDemo regex input"); return; } // Convert new-line (\n) character sequences to new-line characters. args[1] = args[1].replaceAll("\\\\n", "\n"); try { System.out.println("regex = " + args[0]); System.out.println("input = " + args[1]); Pattern p = Pattern.compile(args[0]); Matcher m = p.matcher(args[1]); while (m.find()) System.out.println("Found [" + m.group() + "] starting at " + m.start() + " and ending at " + (m.end() - 1)); } catch (PatternSyntaxException pse) { System.err.println("Bad regex: " + pse.getMessage()); System.err.println("Description: " + pse.getDescription()); System.err.println("Index: " + pse.getIndex()); System.err.println("Incorrect pattern: " + pse.getPattern()); } } }
Das erste , was RegexDemo
‚s main()
Methode tut , ist seine Befehlszeile zu validieren. Dies erfordert zwei Argumente: Das erste Argument ist ein regulärer Ausdruck, und das zweite Argument ist ein Eingabetext, der mit dem regulären Ausdruck abgeglichen werden soll.
Möglicherweise möchten Sie ein neues Zeilenzeichen ( \n
) als Teil des Eingabetextes angeben . Die einzige Möglichkeit, dies zu erreichen, besteht darin, ein \
Zeichen gefolgt von einem n
Zeichen anzugeben . main()
konvertiert diese Zeichenfolge in den Unicode-Wert 10.
Der Großteil des RegexDemo
Codes befindet sich im Konstrukt try
- catch
. Der try
Block gibt zuerst den angegebenen regulären Ausdruck und den eingegebenen Text aus und erstellt dann ein Pattern
Objekt, in dem der kompilierte reguläre Ausdruck gespeichert ist. (Regexes werden kompiliert, um die Leistung während des Mustervergleichs zu verbessern.) Ein Matcher wird aus dem Pattern
Objekt extrahiert und wiederholt nach Übereinstimmungen gesucht, bis keine mehr vorhanden sind. Der catch
Block ruft verschiedene PatternSyntaxException
Methoden auf, um nützliche Informationen über die Ausnahme zu extrahieren. Diese Informationen werden anschließend ausgegeben.
Sie müssen an dieser Stelle nicht mehr über die Funktionsweise des Quellcodes wissen. Dies wird deutlich, wenn Sie die API in Teil 2 untersuchen. Sie müssen jedoch Listing 1 kompilieren. Holen Sie sich den Code aus Listing 1 und geben Sie Folgendes in Ihre Befehlszeile ein, um ihn zu kompilieren RegexDemo
:
javac RegexDemo.java
Muster und seine Konstrukte
Pattern
, die erste von drei Klassen, die die Regex-API umfasst, ist eine kompilierte Darstellung eines regulären Ausdrucks. Pattern
In der SDK-Dokumentation werden verschiedene Regex-Konstrukte beschrieben. Wenn Sie jedoch noch kein begeisterter Regex-Benutzer sind, werden Sie möglicherweise durch Teile der Dokumentation verwirrt. Was sind Quantifizierer und was ist der Unterschied zwischen gierigen , widerstrebenden und besitzergreifenden Quantifizierern? Was sind Zeichenklassen , Grenzübereinstimmungen , Rückverweise und eingebettete Flag-Ausdrücke ? Ich werde diese und weitere Fragen in den nächsten Abschnitten beantworten.
Wörtliche Zeichenfolgen
Das einfachste Regex-Konstrukt ist die Literalzeichenfolge. Ein Teil des Eingabetextes muss mit dem Muster dieses Konstrukts übereinstimmen, um eine erfolgreiche Musterübereinstimmung zu erzielen. Betrachten Sie das folgende Beispiel:
java RegexDemo apple applet
In diesem Beispiel wird versucht festzustellen, ob das apple
Muster im applet
Eingabetext übereinstimmt . Die folgende Ausgabe zeigt die Übereinstimmung:
regex = apple input = applet Found [apple] starting at 0 and ending at 4
Die Ausgabe zeigt uns den regulären Ausdruck und den Eingabetext und zeigt dann eine erfolgreiche Übereinstimmung von apple
innen an applet
. Darüber hinaus stellt sie die Start- und End - Indizes dieser Partie: 0
und 4
, respectively. Der Startindex gibt den ersten Textort an, an dem eine Musterübereinstimmung auftritt. Der Endindex gibt den letzten Textort für die Übereinstimmung an.
Angenommen, wir geben die folgende Befehlszeile an:
java RegexDemo apple crabapple
Dieses Mal erhalten wir die folgende Übereinstimmung mit verschiedenen Start- und Endindizes:
regex = apple input = crabapple Found [apple] starting at 4 and ending at 8
Das umgekehrte Szenario, bei dem applet
es sich um den regulären Ausdruck und apple
den Eingabetext handelt, zeigt keine Übereinstimmung. Die gesamte Regex müssen übereinstimmen, und in diesem Fall wird der Eingabetext enthält keine t
nach apple
.
Metazeichen
Leistungsstärkere Regex-Konstrukte kombinieren wörtliche Zeichen mit Metazeichen. In steht beispielsweise a.b
der Punkt metacharacter ( .
) für ein beliebiges Zeichen, das zwischen a
und angezeigt wird b
. Betrachten Sie das folgende Beispiel:
java RegexDemo .ox "The quick brown fox jumps over the lazy ox."
In diesem Beispiel wird .ox
als regulärer Ausdruck und The quick brown fox jumps over the lazy ox.
als Eingabetext angegeben. RegexDemo
Durchsucht den Text nach Übereinstimmungen, die mit einem beliebigen Zeichen beginnen und mit enden ox
. Es wird die folgende Ausgabe erzeugt:
regex = .ox input = The quick brown fox jumps over the lazy ox. Found [fox] starting at 16 and ending at 18 Found [ ox] starting at 39 and ending at 41
The output reveals two matches: fox
and ox
(with the leading space character). The .
metacharacter matches the f
in the first match and the space character in the second match.
What happens when we replace .ox
with the period metacharacter? That is, what output results from specifying the following command line:
java RegexDemo . "The quick brown fox jumps over the lazy ox."
Because the period metacharacter matches any character, RegexDemo
outputs a match for each character (including the terminating period character) in the input text:
regex = . input = The quick brown fox jumps over the lazy ox. Found [T] starting at 0 and ending at 0 Found [h] starting at 1 and ending at 1 Found [e] starting at 2 and ending at 2 Found [ ] starting at 3 and ending at 3 Found [q] starting at 4 and ending at 4 Found [u] starting at 5 and ending at 5 Found [i] starting at 6 and ending at 6 Found [c] starting at 7 and ending at 7 Found [k] starting at 8 and ending at 8 Found [ ] starting at 9 and ending at 9 Found [b] starting at 10 and ending at 10 Found [r] starting at 11 and ending at 11 Found [o] starting at 12 and ending at 12 Found [w] starting at 13 and ending at 13 Found [n] starting at 14 and ending at 14 Found [ ] starting at 15 and ending at 15 Found [f] starting at 16 and ending at 16 Found [o] starting at 17 and ending at 17 Found [x] starting at 18 and ending at 18 Found [ ] starting at 19 and ending at 19 Found [j] starting at 20 and ending at 20 Found [u] starting at 21 and ending at 21 Found [m] starting at 22 and ending at 22 Found [p] starting at 23 and ending at 23 Found [s] starting at 24 and ending at 24 Found [ ] starting at 25 and ending at 25 Found [o] starting at 26 and ending at 26 Found [v] starting at 27 and ending at 27 Found [e] starting at 28 and ending at 28 Found [r] starting at 29 and ending at 29 Found [ ] starting at 30 and ending at 30 Found [t] starting at 31 and ending at 31 Found [h] starting at 32 and ending at 32 Found [e] starting at 33 and ending at 33 Found [ ] starting at 34 and ending at 34 Found [l] starting at 35 and ending at 35 Found [a] starting at 36 and ending at 36 Found [z] starting at 37 and ending at 37 Found [y] starting at 38 and ending at 38 Found [ ] starting at 39 and ending at 39 Found [o] starting at 40 and ending at 40 Found [x] starting at 41 and ending at 41 Found [.] starting at 42 and ending at 42
Quoting metacharacters
To specify .
or any metacharacter as a literal character in a regex construct, quote the metacharacter in one of the following ways:
- Precede the metacharacter with a backslash character.
- Place the metacharacter between
\Q
and\E
(e.g.,\Q.\E
).
Remember to double each backslash character (as in \\.
or \\Q.\\E
) that appears in a string literal such as String regex = "\\.";
. Don't double the backslash character when it appears as part of a command-line argument.
Character classes
We sometimes need to limit characters that will produce matches to a specific character set. For example, we might search text for vowels a
, e
, i
, o
, and u
, where any occurrence of a vowel indicates a match. A character class identifies a set of characters between square-bracket metacharacters ([ ]
), helping us accomplish this task. Pattern
supports simple, negation, range, union, intersection, and subtraction character classes. We'll look at all of these below.
Simple character class
The simple character class consists of characters placed side by side and matches only those characters. For example, [abc]
matches characters a
, b
, and c
.
Consider the following example:
java RegexDemo [csw] cave
This example matches only c
with its counterpart in cave
, as shown in the following output:
regex = [csw] input = cave Found [c] starting at 0 and ending at 0
Negation character class
The negation character class begins with the ^
metacharacter and matches only those characters not located in that class. For example, [^abc]
matches all characters except a
, b
, and c
.
Consider this example:
java RegexDemo "[^csw]" cave
Note that the double quotes are necessary on my Windows platform, whose shell treats the ^
character as an escape character.
This example matches a
, v
, and e
with their counterparts in cave
, as shown here:
regex = [^csw] input = cave Found [a] starting at 1 and ending at 1 Found [v] starting at 2 and ending at 2 Found [e] starting at 3 and ending at 3
Range character class
The range character class consists of two characters separated by a hyphen metacharacter (-
). All characters beginning with the character on the left of the hyphen and ending with the character on the right of the hyphen belong to the range. For example, [a-z]
matches all lowercase alphabetic characters. It's equivalent to specifying [abcdefghijklmnopqrstuvwxyz]
.
Consider the following example:
java RegexDemo [a-c] clown
This example matches only c
with its counterpart in clown
, as shown:
regex = [a-c] input = clown Found [c] starting at 0 and ending at 0
Merging multiple ranges
You can merge multiple ranges into the same range character class by placing them side by side. For example, [a-zA-Z]
matches all lowercase and uppercase alphabetic characters.
Union character class
The union character class consists of multiple nested character classes and matches all characters that belong to the resulting union. For example, [a-d[m-p]]
matches characters a
through d
and m
through p
.
Consider the following example:
java RegexDemo [ab[c-e]] abcdef
This example matches a
, b
, c
, d
, and e
with their counterparts in abcdef
:
regex = [ab[c-e]] input = abcdef Found [a] starting at 0 and ending at 0 Found [b] starting at 1 and ending at 1 Found [c] starting at 2 and ending at 2 Found [d] starting at 3 and ending at 3 Found [e] starting at 4 and ending at 4
Intersection character class
The intersection character class consists of characters common to all nested classes and matches only common characters. For example, [a-z&&[d-f]]
matches characters d
, e
, and f
.
Consider the following example:
java RegexDemo "[aeiouy&&[y]]" party
Beachten Sie, dass die doppelten Anführungszeichen auf meiner Windows-Plattform erforderlich sind, deren Shell das &
Zeichen als Befehlstrennzeichen behandelt .
Dieses Beispiel stimmt nur y
mit seinem Gegenstück in überein party
:
regex = [aeiouy&&[y]] input = party Found [y] starting at 4 and ending at 4