Регулярни изрази в Java, част 1: Съвпадение на шаблони и клас Pattern

Класовете символи и различни низове на Java предлагат поддръжка на ниско ниво за съвпадение на шаблони, но тази поддръжка обикновено води до сложен код. За по-просто и по-ефективно кодиране, Java предлага Regex API. Този урок от две части ви помага да започнете с регулярни изрази и Regex API. Първо ще разопаковаме трите мощни класа, намиращи се в java.util.regexпакета, след това ще проучим Patternкласа и неговите сложни конструкции за съвпадение на шаблони.

изтегляне Вземете кода Изтеглете изходния код, например приложения в този урок. Създадено от Jeff Friesen за JavaWorld.

Какво представляват регулярните изрази?

А регулярен израз , известен също като регулярен или рег.израз , е низ чийто модел (по образец) описва набор от низове. Моделът определя кои низове принадлежат към набора. Шаблонът се състои от буквални знаци и метасимволи , които са знаци, които имат специално значение вместо буквално значение.

Съвпадение на шаблони е процесът на търсене на текст за идентифициране на съвпадения или низове, които съответстват на шаблона на регулярния израз. Java поддържа съвпадение на шаблони чрез своя Regex API. API се състои от три класа Pattern- Matcher,, и PatternSyntaxException--всички разположени в java.util.regexпакета:

  • Patternобекти, известни също като модели , са компилирани регулярни изрази.
  • Matcherобекти или съвпадения са двигатели, които интерпретират модели за намиране на съвпадения в последователности от символи (обекти, чиито класове реализират java.lang.CharSequenceинтерфейса и служат като текстови източници).
  • PatternSyntaxException обектите описват незаконни модели на регулярни изрази.

Java също така осигурява поддръжка за съвпадение на шаблони чрез различни методи в своя java.lang.Stringклас. Например boolean matches(String regex)връща true само ако извикващият низ съвпада точно regexс регулярния израз на '.

Методи за удобство

Зад кулисите matches()и Stringдруги регекс-ориентирани методи за удобство са внедрени по отношение на Regex API.

RegexDemo

Аз бях създал RegexDemoприложението да се докаже, регулярни изрази в езика и различните методи намира в Pattern, Matcherи PatternSyntaxExceptionкласове. Ето изходния код за демонстрацията:

Листинг 1. Демонстриране на регулярни изрази

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

Първото нещо, което RegexDemoе main()метод прави, е да се валидира своя команден ред. Това изисква два аргумента: първият аргумент е регулярно изражение, а вторият аргумент е входен текст, който трябва да се съпостави с регулярния израз.

Може да искате да посочите \nсимвол на нов ред ( ) като част от въведения текст. Единственият начин да постигнете това е да посочите \символ, последван от nзнак. main()преобразува тази последователност от символи в Unicode стойност 10.

По-голямата част RegexDemoе код се намира в try- catchконструкцията. В tryблока първо извежда определен регулярен и въвеждане на текст и след това създава Patternобект, който съхранява компилиран регулярен. (Регексите се компилират, за да се подобри производителността по време на съвпадение на шаблони.) Съвпадение се извлича от Patternобекта и се използва за многократно търсене на съвпадения, докато не остане нито едно. В catchблока се позовава на различни PatternSyntaxExceptionметоди за извличане на полезна информация за изключението. Впоследствие тази информация се извежда.

В този момент не е нужно да знаете повече за работата на изходния код; ще стане ясно, когато разгледате API в част 2. Трябва обаче да съставите Листинг 1. Вземете кода от Листинг 1, след което въведете следното в командния ред, за да компилирате RegexDemo:

javac RegexDemo.java

Моделът и неговите конструкции

Pattern, първият от трите класа, съдържащ Regex API, е компилирано представяне на регулярен израз. PatternSDK документацията описва различни конструкции на регулярни изрази, но освен ако вече не сте запален потребител на регулярни изрази, може да бъдете объркани от части от документацията. Какво представляват кванторите и каква е разликата между алчни , неохотни и притежаващи квантори? Какво представляват класовете знаци , съвпаденията на граници , препратките назад и вградените изрази на знамена ? Ще отговоря на тези въпроси и още в следващите раздели.

Буквални струни

Най-простата конструкция на регулярни изрази е буквалният низ. Някои части от входния текст трябва да съвпадат с модела на тази конструкция, за да има успешно съвпадение на модела. Помислете за следния пример:

java RegexDemo apple applet

Този пример се опитва да открие дали има съвпадение за appleшаблона във appletвъведения текст. Следният изход разкрива съвпадението:

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

Резултатът ни показва регулярния израз и въведения текст, след което показва успешно съвпадение appleотвътре applet. Освен това той представя началния и крайния индекс на съответствието: 0и 4, съответно. Началният индекс идентифицира първото текстово местоположение, където се получава съвпадение на шаблон; крайният индекс идентифицира последното текстово местоположение за съвпадението.

Сега да предположим, че посочваме следния команден ред:

java RegexDemo apple crabapple

Този път получаваме следния мач с различни начални и крайни индекси:

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

Обратният сценарий, в който appletе регулярният израз и appleе входният текст, не разкрива съвпадение. Целият регулярен израз трябва да съвпада и в този случай входният текст не съдържа tслед apple.

Метасимволи

По-мощните конструкции на регулярни изрази комбинират буквални символи с метасимволи. Например в a.b, периодът metacharacter ( .) представлява всеки символ, който се появява между aи b. Обмислете следния пример:

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

Този пример посочва .oxкато регулярния израз и The quick brown fox jumps over the lazy ox.като входен текст. RegexDemoтърси в текста съвпадения, които започват с произволен знак и завършват с ox. Той дава следния изход:

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

Имайте предвид, че двойните кавички са необходими на моята платформа на Windows, чиято обвивка третира &символа като разделител на команди.

Този пример съвпада само yс неговия аналог в party:

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