Regeluttryck i Java, del 1: Mönstermatchning och mönsterklassen

Java karaktär och diverse strängklasser erbjuder stöd på låg nivå för mönstermatchning, men det stödet leder vanligtvis till komplex kod. För enklare och effektivare kodning erbjuder Java Regex API. Denna tvådelade handledning hjälper dig att komma igång med reguljära uttryck och Regex API. Först packar vi upp de tre kraftfulla klasserna i java.util.regexpaketet, sedan utforskar vi Patternklassen och dess sofistikerade mönstermatchningskonstruktioner.

ladda ner Skaffa koden Ladda ner källkoden till exempel applikationer i denna handledning. Skapad av Jeff Friesen för JavaWorld.

Vad är reguljära uttryck?

Ett vanligt uttryck , även känt som en regex eller regexp , är en sträng vars mönster (mall) beskriver en uppsättning strängar. Mönstret avgör vilka strängar som tillhör uppsättningen. Ett mönster består av bokstavliga tecken och metatecken , vilket är tecken som har särskild betydelse istället för en bokstavlig betydelse.

Mönstermatchning är processen att söka text för att identifiera matchningar eller strängar som matchar ett regexmönster. Java stöder mönstermatchning via Regex API. API består av tre classes-- Pattern, Matcheroch PatternSyntaxException--Alla ligger i java.util.regexpaketet:

  • Patternobjekt, även kända som mönster , är sammanställda regexes.
  • Matcherobjekt eller matchare är motorer som tolkar mönster för att hitta matchningar i karaktärssekvenser (objekt vars klasser implementerar java.lang.CharSequencegränssnittet och fungerar som textkällor).
  • PatternSyntaxException objekt beskriver olagliga regexmönster.

Java ger också stöd för mönstermatchning via olika metoder i sin java.lang.Stringklass. Till exempel boolean matches(String regex)returnerar true endast om den anropande strängen exakt matchar regexregex.

Bekvämhetsmetoder

Bakom kulisserna matches()och Stringandra regex-orienterade bekvämlighetsmetoder implementeras i termer av Regex API.

RegexDemo

Jag har skapat RegexDemoprogrammet för att visa Javas reguljära uttryck och de olika metoderna som finns i Pattern, Matcheroch PatternSyntaxExceptionklasser. Här är källkoden för demo:

Listning 1. Demonstrera regexer

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

Det första RegexDemoär main()metoden gör är att validera sin kommandoraden. Detta kräver två argument: det första argumentet är en regex, och det andra argumentet är inmatningstext som ska matchas mot regex.

Du kanske vill ange en ny rad ( \n) som en del av inmatningstexten. Det enda sättet att åstadkomma detta är att ange ett \tecken följt av ett ntecken. main()konverterar denna teckenföljd till Unicode-värde 10.

Huvuddelen av RegexDemokoden finns i try- catchkonstruktionen. Det tryblocket utmatar först den specificerade regex och mata in text och skapar en sedan Patternobjekt som lagrar den kompilerade regex. (Regexes sammanställs för att förbättra prestanda under mönstermatchning.) En matcher extraheras från Patternobjektet och används för att upprepade gånger söka efter matchningar tills ingen finns kvar. Den catchblocket anropar olika PatternSyntaxExceptionmetoder för att extrahera användbar information om undantag. Denna information matas sedan ut.

Du behöver inte veta mer om källkodens funktion just nu; det kommer att bli tydligt när du utforskar API: et i del 2. Du måste dock sammanställa Listing 1. Hämta koden från Listing 1 och skriv sedan följande i din kommandorad för att kompilera RegexDemo:

javac RegexDemo.java

Mönster och dess konstruktioner

Pattern, den första av tre klasser som innehåller Regex API, är en sammanställd representation av ett reguljärt uttryck. PatternSDK-dokumentationen beskriver olika regex-konstruktioner, men om du inte redan är en ivrig regex-användare kan du bli förvirrad av delar av dokumentationen. Vad är kvantifierare och vad är skillnaden mellan giriga , motvilliga och besittande kvantifierare? Vad är karaktärsklasser , gräns matchers , bakåtreferenser och inbäddade flaggan uttryck ? Jag kommer att besvara dessa frågor och mer i nästa avsnitt.

Bokstavliga strängar

Den enklaste regex-konstruktionen är den bokstavliga strängen. En del av inmatningstexten måste matcha konstruktionsmönstret för att ha en lyckad mönstermatchning. Tänk på följande exempel:

java RegexDemo apple applet

Detta exempel försöker upptäcka om det finns en matchning för applemönstret i appletinmatningstexten. Följande utdata avslöjar matchningen:

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

Utgången visar oss regex- och inmatningstexten och indikerar sedan en lyckad matchning appleinom applet. Dessutom presenterar den start- och slutindex för den matchen: 0och 4respektive. Startindexet identifierar den första textplatsen där en mönstermatchning förekommer. slutindexet identifierar den sista textplatsen för matchningen.

Antag nu att vi anger följande kommandorad:

java RegexDemo apple crabapple

Den här gången får vi följande matchning med olika start- och slutindex:

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

Det omvända scenariot, som appletär regex och appleär inmatningstexten, avslöjar ingen matchning. Hela regex måste matcha, och i det här fallet innehåller ingångstexten inte ett tefter apple.

Metatecken

Kraftfullare regex-konstruktioner kombinerar bokstavliga karaktärer med metatecken. Till exempel representerar a.bperiodens metatecken ( .) alla tecken som visas mellan aoch b. Tänk på följande exempel:

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

Detta exempel specificeras .oxsom regex och The quick brown fox jumps over the lazy ox.som inmatningstext. RegexDemosöker i texten efter matchningar som börjar med vilken karaktär som helst och slutar med ox. Det ger följande utdata:

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

Observera att dubbla citat är nödvändiga på min Windows-plattform, vars skal behandlar &karaktären som en kommandoseparator.

Detta exempel matchar endast ymed motsvarigheten i party:

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