Expressions régulières en Java, Partie 1: Correspondance de modèles et classe Pattern

Les classes de caractères et de chaînes assorties de Java offrent une prise en charge de bas niveau pour la correspondance de modèles, mais cette prise en charge conduit généralement à un code complexe. Pour un codage plus simple et plus efficace, Java propose l'API Regex. Ce didacticiel en deux parties vous aide à vous familiariser avec les expressions régulières et l'API Regex. Nous allons d'abord décompresser les trois classes puissantes résidant dans le java.util.regexpackage, puis nous explorerons la Patternclasse et ses constructions sophistiquées de correspondance de motifs.

télécharger Obtenir le code Téléchargez le code source des exemples d'applications dans ce didacticiel. Créé par Jeff Friesen pour JavaWorld.

Que sont les expressions régulières?

Une expression régulière , également appelée regex ou regexp , est une chaîne dont le modèle (modèle) décrit un ensemble de chaînes. Le modèle détermine quelles chaînes appartiennent à l'ensemble. Un modèle se compose de caractères littéraux et de métacaractères , qui sont des caractères qui ont une signification spéciale au lieu d'une signification littérale.

La correspondance de modèle est le processus de recherche de texte pour identifier les correspondances ou les chaînes qui correspondent au modèle d'une expression régulière. Java prend en charge la correspondance de modèles via son API Regex. L'API se compose de trois classes Pattern- Matcher, et - toutes PatternSyntaxExceptionsituées dans le java.util.regexpackage:

  • Patternles objets, également appelés modèles , sont des expressions rationnelles compilées.
  • MatcherLes objets, ou matchers , sont des moteurs qui interprètent des modèles pour localiser des correspondances dans des séquences de caractères (objets dont les classes implémentent l' java.lang.CharSequenceinterface et servent de sources de texte).
  • PatternSyntaxException les objets décrivent des modèles de regex illégaux.

Java prend également en charge la correspondance de modèles via diverses méthodes de sa java.lang.Stringclasse. Par exemple, boolean matches(String regex)renvoie true uniquement si la chaîne d'appel correspond exactement à regexl'expression rationnelle de.

Méthodes de commodité

Dans les coulisses, matches()et Stringles autres méthodes pratiques orientées regex sont implémentées en termes de l'API Regex.

RegexDemo

J'ai créé l' RegexDemoapplication afin de démontrer les expressions régulières de Java et les différentes méthodes situées dans le Pattern, Matcheret les PatternSyntaxExceptionclasses. Voici le code source de la démo:

Listing 1. Démonstration des expressions rationnelles

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 première chose que fait RegexDemola main()méthode est de valider sa ligne de commande. Cela nécessite deux arguments: le premier argument est une expression régulière et le deuxième argument est un texte d'entrée à mettre en correspondance avec l'expression régulière.

Vous souhaiterez peut-être spécifier un caractère de nouvelle ligne ( \n) dans le cadre du texte d'entrée. La seule façon d'y parvenir est de spécifier un \caractère suivi d'un ncaractère. main()convertit cette séquence de caractères en valeur Unicode 10.

La majeure partie du RegexDemocode de 's se trouve dans la construction try- catch. Le trybloc produit d'abord l'expression rationnelle spécifiée et le texte d'entrée, puis crée un Patternobjet qui stocke l'expression rationnelle compilée. (Les expressions rationnelles sont compilées pour améliorer les performances lors de la correspondance de modèles.) Une correspondance est extraite de l' Patternobjet et utilisée pour rechercher à plusieurs reprises des correspondances jusqu'à ce qu'il n'en reste aucune. Le catchbloc appelle diverses PatternSyntaxExceptionméthodes pour extraire des informations utiles sur l'exception. Ces informations sont ensuite sorties.

Vous n'avez pas besoin d'en savoir plus sur le fonctionnement du code source à ce stade; cela deviendra clair lorsque vous explorerez l'API dans la partie 2. Vous devez cependant compiler le listing 1. Récupérez le code de la liste 1, puis tapez ce qui suit dans votre ligne de commande pour compiler RegexDemo:

javac RegexDemo.java

Motif et ses constructions

Pattern, la première des trois classes comprenant l'API Regex, est une représentation compilée d'une expression régulière. PatternLa documentation du SDK décrit diverses constructions de regex, mais à moins que vous ne soyez déjà un utilisateur avide de regex, vous pourriez être dérouté par certaines parties de la documentation. Que sont les quantificateurs et quelle est la différence entre les quantificateurs gourmands , réticents et possessifs ? Que sont les classes de caractères , les correspondances de limites , les références arrière et les expressions d'indicateur incorporées ? Je répondrai à ces questions et à d'autres dans les sections suivantes.

Chaînes littérales

La construction regex la plus simple est la chaîne littérale. Une partie du texte d'entrée doit correspondre au modèle de cette construction afin d'avoir une correspondance de modèle réussie. Prenons l'exemple suivant:

java RegexDemo apple applet

Cet exemple tente de découvrir s'il existe une correspondance pour le applemodèle dans le applettexte d'entrée. La sortie suivante révèle la correspondance:

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

La sortie nous montre l'expression régulière et le texte d'entrée, puis indique une correspondance réussie de l' appleintérieur applet. De plus, il présente les index de début et de fin de cette correspondance: 0et 4, respectivement. L'index de départ identifie le premier emplacement de texte où une correspondance de modèle se produit; l'index de fin identifie le dernier emplacement du texte pour la correspondance.

Supposons maintenant que nous spécifions la ligne de commande suivante:

java RegexDemo apple crabapple

Cette fois, nous obtenons la correspondance suivante avec différents index de début et de fin:

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

Le scénario inverse, dans lequel se applettrouve l'expression régulière et applele texte d'entrée, ne révèle aucune correspondance. L'ensemble de l'expression régulière doit correspondre, et dans ce cas, le texte d'entrée ne contient pas de tafter apple.

Métacaractères

Des constructions regex plus puissantes combinent des caractères littéraux avec des métacaractères. Par exemple, dans a.b, le point metacharacter ( .) représente tout caractère qui apparaît entre aet b. Prenons l'exemple suivant:

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

Cet exemple spécifie .oxcomme expression régulière et The quick brown fox jumps over the lazy ox.comme texte d'entrée. RegexDemorecherche dans le texte les correspondances commençant par n'importe quel caractère et se terminant par ox. Il produit la sortie suivante:

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

Notez que les guillemets sont nécessaires sur ma plate-forme Windows, dont le shell traite le &caractère comme un séparateur de commande.

Cet exemple ne correspond qu'à yson homologue dans party:

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