Wyrażenia regularne w Javie, część 1: Dopasowywanie wzorców i klasa Pattern

Znaki Java i różne klasy ciągów oferują niskopoziomową obsługę dopasowywania wzorców, ale ta obsługa zazwyczaj prowadzi do złożonego kodu. W celu prostszego i wydajniejszego kodowania Java oferuje interfejs API Regex. Ten dwuczęściowy samouczek pomaga rozpocząć korzystanie z wyrażeń regularnych i interfejsu API Regex. Najpierw rozpakujemy trzy potężne klasy znajdujące się w java.util.regexpakiecie, a następnie zbadamy tę Patternklasę i jej wyrafinowane konstrukcje dopasowywania wzorców.

pobierz Pobierz kod Pobierz kod źródłowy na przykład aplikacje w tym samouczku. Stworzone przez Jeffa Friesena dla JavaWorld.

Co to są wyrażenia regularne?

Wyrażenie regularne , znany również jako regex lub regexp , jest ciągiem znaków, którego wzór (szablon) opisuje zbiór łańcuchów. Wzorzec określa, które ciągi należą do zestawu. Wzorzec składa się z literalnych znaków i metaznaków , które są znakami, które mają specjalne znaczenie zamiast dosłownego znaczenia.

Dopasowywanie wzorca to proces wyszukiwania tekstu w celu zidentyfikowania dopasowań lub ciągów pasujących do wzorca wyrażenia regularnego. Java obsługuje dopasowywanie wzorców za pośrednictwem swojego interfejsu API Regex. Interfejs API składa się z trzech klas Pattern- Matcher, i - wszystkich PatternSyntaxExceptionznajdujących się w java.util.regexpakiecie:

  • Patternobiekty, znane również jako wzorce , to skompilowane wyrażenia regularne.
  • Matcherobiekty lub elementy dopasowujące to silniki, które interpretują wzorce w celu wyszukiwania dopasowań w sekwencjach znaków (obiekty, których klasy implementują java.lang.CharSequenceinterfejs i służą jako źródła tekstu).
  • PatternSyntaxException obiekty opisują niedozwolone wzorce regex.

Java zapewnia również obsługę dopasowywania wzorców za pomocą różnych metod w swojej java.lang.Stringklasie. Na przykład boolean matches(String regex)zwraca prawdę tylko wtedy, gdy wywołujący ciąg dokładnie pasuje regexdo wyrażenia regularnego.

Wygodne metody

Za kulisami matches()i Stringinne wygodne metody zorientowane na wyrażenia regularne są zaimplementowane w oparciu o Regex API.

RegexDemo

Mam stworzył RegexDemoaplikację do wykazania wyrażeń regularnych Java i różne metody znajdujące się w Pattern, Matcheri PatternSyntaxExceptionklas. Oto kod źródłowy wersji demonstracyjnej:

Listing 1. Demonstrowanie wyrażeń regularnych

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

Pierwszą rzeczą RegexDemojest main()metoda robi to, aby potwierdzić swoją linię poleceń. Wymaga to dwóch argumentów: pierwszy argument to wyrażenie regularne, a drugi argument to tekst wejściowy do dopasowania względem wyrażenia regularnego.

Możesz chcieć określić znak nowej linii ( \n) jako część tekstu wejściowego. Jedynym sposobem na osiągnięcie tego jest określenie \znaku, po którym następuje nznak. main()konwertuje tę sekwencję znaków na wartość Unicode 10.

Większość RegexDemokodu znajduje się w konstrukcji try- catch. tryBlok najpierw wyprowadza Podany regex i tekstowym, a następnie tworzy Patternobiekt, który przechowuje skompilowane regex. (Regeksy są kompilowane, aby poprawić wydajność podczas dopasowywania wzorców). Dopasowanie jest wyodrębniane z Patternobiektu i używane do wielokrotnego wyszukiwania dopasowań, dopóki żadne nie pozostanie. catchBlok wywołuje różnych PatternSyntaxExceptionmetod wyodrębniania użytecznych informacji o wyjątku. Ta informacja jest następnie wyprowadzana.

W tym momencie nie musisz wiedzieć więcej o działaniu kodu źródłowego; stanie się jasne, kiedy zapoznasz się z API w części 2. Jednak musisz skompilować Listing 1. Pobierz kod z listy 1, a następnie wpisz w wierszu poleceń, aby skompilować RegexDemo:

javac RegexDemo.java

Wzór i jego konstrukcje

Pattern, pierwsza z trzech klas obejmujących Regex API, jest skompilowaną reprezentacją wyrażenia regularnego. PatternDokumentacja SDK opisuje różne konstrukcje wyrażeń regularnych, ale jeśli nie jesteś już zapalonym użytkownikiem wyrażeń regularnych, możesz być zdezorientowany fragmentami dokumentacji. Czym są kwantyfikatory i jaka jest różnica między kwantyfikatorami chciwymi , niechętnymi i zaborczymi ? Co to są klasy znaków , dopasowania granic , odwołania wsteczne i wyrażenia flag osadzonych ? Odpowiem na te i inne pytania w następnych sekcjach.

Dosłowne ciągi

Najprostszą konstrukcją wyrażenia regularnego jest literał łańcuchowy. Pewna część tekstu wejściowego musi pasować do wzorca tej konstrukcji, aby uzyskać pomyślne dopasowanie wzorca. Rozważmy następujący przykład:

java RegexDemo apple applet

Ten przykład próbuje wykryć, czy istnieje zgodność dla applewzorca w applettekście wejściowym. Poniższe dane wyjściowe ujawniają dopasowanie:

regex = apple input = applet Found [apple] starting at 0 and ending at 4

Dane wyjściowe pokazują nam wyrażenie regularne i tekst wejściowy, a następnie wskazują udane dopasowanie applewewnątrz applet. Dodatkowo przedstawia początkowe i końcowe indeksy tego dopasowania: 0i 4, odpowiednio. Indeks początkowy identyfikuje pierwszą lokalizację tekstu, w której występuje dopasowanie wzorca; indeks końcowy określa ostatnią lokalizację tekstu dopasowania.

Teraz załóżmy, że określimy następującą linię poleceń:

java RegexDemo apple crabapple

Tym razem otrzymujemy następujące dopasowanie z różnymi indeksami początkowymi i końcowymi:

regex = apple input = crabapple Found [apple] starting at 4 and ending at 8

Odwrotny scenariusz, w którym appletjest wyrażeniem regularnym i appletekstem wejściowym, nie ujawnia żadnego dopasowania. Całe wyrażenie regularne musi być zgodne, aw tym przypadku tekst wejściowy nie zawiera tpo apple.

Metaznaki

Bardziej zaawansowane konstrukcje wyrażeń regularnych łączą znaki dosłowne z metaznakami. Na przykład w a.bprogramie kropka metaznak ( .) reprezentuje dowolny znak, który pojawia się między aa b. Rozważmy następujący przykład:

java RegexDemo .ox "The quick brown fox jumps over the lazy ox."

W tym przykładzie określono .oxjako wyrażenie regularne i The quick brown fox jumps over the lazy ox.tekst wejściowy. RegexDemoprzeszukuje tekst pod kątem dopasowań rozpoczynających się dowolnym znakiem i kończących się na ox. Generuje następujący wynik:

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

Zauważ, że podwójne cudzysłowy są konieczne na mojej platformie Windows, której powłoka traktuje &znak jako separator poleceń.

Ten przykład pasuje tylko ydo swojego odpowiednika w party:

regex = [aeiouy&&[y]] input = party Found [y] starting at 4 and ending at 4