Espressioni regolari in Java, Parte 1: corrispondenza del modello e classe del modello

Le classi di caratteri e stringhe assortite di Java offrono un supporto di basso livello per la corrispondenza dei modelli, ma quel supporto in genere porta a codice complesso. Per una codifica più semplice ed efficiente, Java offre l'API Regex. Questo tutorial in due parti ti aiuta a iniziare con le espressioni regolari e l'API Regex. Per prima cosa decomprimeremo le tre potenti classi che risiedono nel java.util.regexpacchetto, quindi esploreremo la Patternclasse ei suoi sofisticati costrutti di corrispondenza dei modelli.

download Ottieni il codice Scarica il codice sorgente per applicazioni di esempio in questo tutorial. Creato da Jeff Friesen per JavaWorld.

Cosa sono le espressioni regolari?

Un'espressione regolare , noto anche come una regex o regexp , è una stringa il cui modello (template) descrive un insieme di stringhe. Il pattern determina quali stringhe appartengono al set. Un modello è costituito da caratteri letterali e metacaratteri , che sono caratteri che hanno un significato speciale invece di un significato letterale.

La corrispondenza del modello è il processo di ricerca del testo per identificare le corrispondenze o le stringhe che corrispondono al modello di un'espressione regolare. Java supporta il pattern matching tramite la sua API Regex. L'API consiste di tre classes-- Pattern, Matchere PatternSyntaxException--tutti situato nel java.util.regexpacchetto:

  • Patterngli oggetti, noti anche come pattern , sono espressioni regolari compilate.
  • Matchergli oggetti, o matcher , sono motori che interpretano modelli per individuare corrispondenze in sequenze di caratteri (oggetti le cui classi implementano l' java.lang.CharSequenceinterfaccia e fungono da sorgenti di testo).
  • PatternSyntaxException gli oggetti descrivono modelli di espressioni regolari illegali.

Java fornisce anche il supporto per la corrispondenza dei modelli tramite vari metodi nella sua java.lang.Stringclasse. Ad esempio, boolean matches(String regex)restituisce true solo se la stringa di richiamo corrisponde esattamente regexalla regex di.

Metodi di convenienza

Dietro le quinte, matches()e String's altri metodi di convenienza regex-oriented sono implementate in termini di API Regex.

RegexDemo

Ho creato l' RegexDemoapplicazione per dimostrare le espressioni di Java regolari e i vari metodi situati nel Pattern, Matchere PatternSyntaxExceptionle classi. Ecco il codice sorgente per la demo:

Listato 1. Dimostrazione di regex

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

La prima cosa RegexDemoche main()fa il metodo è convalidare la sua riga di comando. Ciò richiede due argomenti: il primo argomento è una regex e il secondo argomento è il testo di input da confrontare con la regex.

Potrebbe essere necessario specificare un carattere new-line ( \n) come parte del testo di input. L'unico modo per ottenere ciò è specificare un \carattere seguito da un ncarattere. main()converte questa sequenza di caratteri nel valore Unicode 10.

La maggior parte del RegexDemocodice di si trova nel costrutto try- catch. Il tryblocco prima restituisce la regex specificata e il testo di input, quindi crea un Patternoggetto che memorizza la regex compilata. (Le espressioni regolari vengono compilate per migliorare le prestazioni durante la corrispondenza dei modelli.) Un abbinatore viene estratto Patterndall'oggetto e utilizzato per cercare ripetutamente le corrispondenze finché non ne rimane nessuna. Il catchblocco richiama vari PatternSyntaxExceptionmetodi per estrarre informazioni utili sull'eccezione. Queste informazioni vengono successivamente emesse.

A questo punto non è necessario saperne di più sul funzionamento del codice sorgente; diventerà chiaro quando esplori l'API nella Parte 2. Tuttavia, devi compilare il Listato 1. Prendi il codice dal Listato 1, quindi digita quanto segue nella riga di comando per compilare RegexDemo:

javac RegexDemo.java

Pattern e suoi costrutti

Pattern, la prima di tre classi che comprendono l'API Regex, è una rappresentazione compilata di un'espressione regolare. PatternLa documentazione dell'SDK descrive vari costrutti di regex, ma a meno che tu non sia già un avido utente di espressioni regolari, potresti essere confuso da parti della documentazione. Cosa sono i quantificatori e qual è la differenza tra quantificatori avidi , riluttanti e possessivi ? Cosa sono le classi di caratteri , i parametri di confronto , i riferimenti all'indietro e le espressioni di flag incorporate ? Risponderò a queste e ad altre domande nelle prossime sezioni.

Stringhe letterali

Il costrutto regex più semplice è la stringa letterale. Alcune parti del testo di input devono corrispondere al modello di questo costrutto per avere una corrispondenza del modello di successo. Considera il seguente esempio:

java RegexDemo apple applet

Questo esempio tenta di scoprire se esiste una corrispondenza per il applemodello nel applettesto di input. Il seguente output rivela la corrispondenza:

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

L'output ci mostra la regex e il testo di input, quindi indica una corrispondenza riuscita di appleinside applet. Inoltre, presenta gli indici di inizio e fine di quella corrispondenza: 0e 4, rispettivamente. L'indice iniziale identifica la prima posizione del testo in cui si verifica una corrispondenza di pattern; l'indice finale identifica l'ultima posizione del testo per la corrispondenza.

Supponiamo ora di specificare la seguente riga di comando:

java RegexDemo apple crabapple

Questa volta, otteniamo la seguente corrispondenza con diversi indici di inizio e di fine:

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

Lo scenario inverso, in cui appletè la regex ed appleè il testo di input, non rivela alcuna corrispondenza. L'intera regex deve corrispondere e in questo caso il testo di input non contiene un tdopo apple.

Metacaratteri

I costrutti regex più potenti combinano caratteri letterali con metacaratteri. Ad esempio, in a.b, il punto metacarattere ( .) rappresenta qualsiasi carattere che appare tra ae b. Considera il seguente esempio:

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

Questo esempio specifica .oxcome regex e The quick brown fox jumps over the lazy ox.come testo di input. RegexDemocerca nel testo corrispondenze che iniziano con qualsiasi carattere e finiscono con ox. Produce il seguente output:

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

Nota che le virgolette doppie sono necessarie sulla mia piattaforma Windows, la cui shell tratta il &carattere come un separatore di comandi.

Questo esempio corrisponde solo yalla sua controparte in party:

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