Développons en Java
v 2.40   Copyright (C) 1999-2023 .   
22. L'API Stream Partie 3 : Les API avancées Imprimer Index Index avec sommaire Télécharger le PDF

 

23. Les expressions régulières

 

chapitre    2 3

 

Niveau : niveau 3 Intermédiaire 

 

Historiquement, les expressions régulières introduites en Java 1.4 sont la forme la plus ancienne de pattern matching proposée par Java.

Les expressions régulières, aussi appelées expressions rationnelles ou abrégées en regex, permettent de rechercher un motif avec une syntaxe particulière dans du texte.

Les cas d'usage sont nombreux notamment :

Une expression régulière est un motif sous la forme d'une chaîne de caractères qui décrit avec une syntaxe particulière un ensemble de caractères dont la correspondance exacte ou multiple pourra être recherchée dans une chaîne de caractères.

Le motif de recherche peut être un simple caractère, une chaîne fixe ou une expression complexe contenant des caractères spéciaux composant le motif.

Exemple : une expression régulière qui correspond à un ensemble d'un ou plusieurs chiffres

Exemple :
    String MOTIF_NOMBRE = "[0-9]+";

Les expressions régulières peuvent être plus ou moins complexes selon les besoins.

Exemple :
    String MOTIF_IPV4 = "^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\."
        + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\."
        + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\."
        + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$";

Les expressions régulières sont supportées par de nombreux langages et outils malheureusement avec une syntaxe parfois différente ou des fonctionnalités dédiées. Dans le monde des expressions régulières, il existe différentes syntaxes proposées par différents langages ou outils, tels que Perl, Python, PHP, grep, awk, sed, etc . La syntaxe d'une expression régulière n'est donc pas forcément portable. La syntaxe des expressions régulières en Java est très similaire à celle que l'on trouve en Perl.

Le motif qui est défini par une expression régulière peut correspondre :

Ce chapitre contient plusieurs sections :

 

23.1. Le package java.util.regex

Le support des expressions régulières est proposé dans les types du package java.util.regex qui contient :

La mise en oeuvre des expressions régulières combine l'utilisation des classes Pattern et Matcher :

 

23.1.1. La classe Pattern

La classe Pattern encapsule une représentation compilée d'une expression régulière.

Une expression régulière, fournie sous la forme d'une chaîne de caractères doit être compilée et encapsulée dans une instance de type Pattern.

La classe Pattern propose plusieurs méthodes :

Méthode

Rôle

Predicate<String> asPredicate()

Renvoyer un Predicate qui teste si le motif est trouvé dans une chaîne d'entrée donnée en utilisant la méthode find() d'une instance de Matcher (depuis Java 8)

Predicate<String> asMatchPredicate()

Renvoyer un Predicate qui teste si le motif correspond à la chaîne d'entrée donnée en utilisant la méthode matcher() d'une instance de Matcher (depuis Java 11)

static Pattern compile(String regex)

Compiler l'expression régulière fournie et retourner une instance de type Pattern qui l'encapsule

static Pattern compile(String regex, int flags)

Renvoyer une instance de type Pattern qui encapsule une version compilée de l'expression régulière et les options fournies en paramètres

int flags()

Renvoyer les options de correspondance utilisées

Matcher matcher(CharSequence input)

Renvoyer une instance de type Matcher qui applique l'expression sur la chaîne fournie en paramètre

static boolean matches(String regex, CharSequence input)

Compiler l'expression fournie et l'appliquer sur la chaîne fournie en paramètre pour renvoyer le résultat de la correspondance

String pattern()

Renvoyer l'expression régulière utilisée pour créer l'instance

static String quote(String s)

Renvoyer une chaîne littérale du motif pour la chaîne fournie en paramètre (depuis Java 1.5)

String[] split(CharSequence input)

Découper la chaîne de caractères en utilisant l'expression régulière comme séparateur

String[] split(CharSequence input, int limit)

Découper la chaîne de caractères en utilisant l'expression régulière comme séparateur avec un nombre maximum de sous-chaînes

Stream<String> splitAsStream(CharSequence input)

Obtenir un Stream des éléments résultant du découpage de la chaîne avec l'expression régulière comme séparateur (depuis Java 8)

String toString()

Renvoyer une représentation textuelle de l'instance sous la forme de chaîne de caractères du motif. Il s'agit de l'expression régulière à partir de laquelle le motif a été compilé.


La méthode static quote() renvoie un motif littéral pour la chaîne de caractères fournie en paramètre. Elle produit une chaîne qui peut être utilisée pour créer un motif qui correspondrait à la chaîne comme s'il s'agissait d'un motif littéral. Les métacaractères ou les séquences d'échappement dans la séquence d'entrée n'auront pas de signification particulière.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    System.out.println(Pattern.quote("[a-z]"));
  }
}

Résultat :
\Q[a-z]\E

 

23.1.1.1. L'obtention d'une instance de type Pattern

La classe Pattern ne propose aucun constructeur public.

Pour obtenir une instance de type Pattern, il faut invoquer une des surcharges de la fabrique compile(). Le premier paramètre de ces surcharges est le motif de l'expression régulière.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    Pattern pattern = Pattern.compile("[0-9]*");
  }
}

La classe Pattern est immuable et thread-safe.

Une des surcharges de la méthode compile attend en second paramètre un entier qui permet de préciser une ou plusieurs options lors de l'application de l'expression régulière.

Exemple :

    Pattern pattern = Pattern.compile("[a-z]*",Pattern.CASE_INSENSITIVE);

 

23.1.1.2. L'obtention d'une instance de type Matcher

La méthode matcher() permet d'obtenir une instance de type Matcher qui va utiliser l'expression régulière compilée.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    Pattern pattern = Pattern.compile("[a-z]*",Pattern.CASE_INSENSITIVE); 
    Matcher matcher = pattern.matcher("Java");
  }
}

 

23.1.1.3. La méthode matches()

La méthode static matches() est proposée pour faciliter l'utilisation une seule fois d'une expression régulière. Elle compile l'expression et l'applique sur la chaîne fournie en paramètre en une seule invocation pour vérifier la correspondance du motif sur la chaîne

La méthode matches() renvoie un booléen qui renvoie le résultat de la correspondance avec l'application de l'expression régulière sur la chaîne de caractères passées en paramètres.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    boolean resultat = Pattern.matches("[0-9]*", "1234");
    System.out.println(resultat);
  }
}

Résultat :
true

L'exemple ci-dessus est équivalent à

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    Pattern pattern = Pattern.compile("[0-9]*");
    Matcher matcher = pattern.matcher("1234");
    boolean resultat = matcher.matches();
    System.out.println(resultat);
  }
}

Résultat :
true

Le résultat de la correspondance dépend du motif et la chaîne de caractères fournis.

Exemple :
    boolean trouve = Pattern.matches("bc", "abcd");

Exemple :
  Pattern pattern = Pattern.compile("bc");
  Matcher matcher = pattern.matcher("abcd");
  boolean trouve = matcher.matches();


Résultat :
false

La méthode matches() recompile l'expression à chaque invocation : elle n'est donc pas performante en cas d'invocations répétées. Dans ce cas, il est préférable de compiler l'expression et de réutiliser l'instance de type Pattern obtenue.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    boolean trouve = Pattern.matches("Java", "Java");
    System.out.println(trouve);
    trouve = Pattern.matches("Java", "Java utilise les regex");
    System.out.println(trouve);
  }
}

Résultat :
true
false

Dans ce cas, il est préférable de compiler l'expression et de réutilisé l'instance de type Pattern obtenue.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher("Java");
    boolean trouve = matcher.matches();
    System.out.println(trouve);
 
    matcher = pattern.matcher("Java utilise les regex");
    trouve = matcher.matches();
    System.out.println(trouve);
  }
}

Résultat :
true
false

 

23.1.1.4. Le découpage d'une chaîne selon un motif

La méthode split() permet de découper une chaîne en plusieurs sous-chaînes grâce à un délimiteur défini par l'expression régulière. Elle retourne un tableau de String qui contient les sous-chaînes.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern p = Pattern.compile(":");
    String[] elements = p.split("element1:element2:element3");
    for (String element : elements) {
      System.out.println(element);
    }
  }
}

Résultat :
element1
element2
element3

La surcharge de la méthode split() qui attend en second paramètre un entier limit permet de définir le nombre maximum de sous-chaînes obtenues.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern p = Pattern.compile(":");
    String[] elements = p.split("element1:element2:element3", 2);
    for (String element : elements) {
      System.out.println(element);
    }
  }
}

Résultat :
element1
element2:element3

 

23.1.2. La classe Matcher

La classe Matcher est un moteur d'application d'une expression régulière sur une chaîne de caractères et permet de restituer les informations issues de la correspondance trouvée.

La classe Matcher implémente l'interface MatchResult.

Elle ne possède aucun constructeur public. Pour obtenir une instance, il faut invoquer la méthode matcher() sur une instance de type Pattern en lui passant en paramètre la chaîne de caractères à traiter.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "1234";
    Pattern pattern = Pattern.compile("[0-9]*");
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Une instance de type Matcher encapsule l'état de la correspondance en cours : elle n'est donc pas thread-safe. Par contre, il est possible d'obtenir plusieurs instances de type Matcher à partir d'une même instance de type Pattern.

 

23.1.2.1. Les méthodes pour obtenir des index

Plusieurs méthodes permettent d'obtenir des index sur la précédente correspondance.

Méthode

Rôle

int start()

Renvoyer l'index de début de la précédente correspondance

int start(int group)

Renvoyer l'index de début du groupe capturé dont le numéro est fourni en paramètre de la précédente correspondance

int end()

Renvoyer l'index de fin de la précédente correspondance

public int end(int group)

Renvoyer l'index de fin du groupe capturé dont le numéro est fourni en paramètre de la précédente correspondance


Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "Java ou Java ou Java";
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc+" : "+texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
2 : Java
3 : Java
Nb occurrences = 3

 

23.1.2.2. Les méthodes pour la recherche de correspondance

Les méthodes pour la recherche de correspondance analysent la chaîne de caractères d'entrée et renvoient un booléen indiquant si le motif est trouvé ou non :

Méthode

Rôle

boolean lookingAt()

Renvoyer un booléen qui indique si le motif est trouvé dans une partie de la chaîne de caractères

public boolean find()

Renvoyer un booléen si le motif est encore trouvé dans le reste du parcours de la chaîne de caractères

public boolean find(int start)

Renvoyer un booléen qui indique si le motif est trouvé à partir de l'index de début fourni en paramètre

public boolean matches()

Renvoyer un booléen qui indique si le motif est trouvé dans l'intégralité de la chaîne de caractères

int start()

Renvoyer l'index de début de la sous-chaîne capturée

int end()

Renvoyer l'index de fin de la sous-chaîne capturée


Les méthodes matches() et lookingAt() tentent toutes deux de faire correspondre l'expression régulière sur la chaîne de caractères. La différence réside dans le fait que matches() requiert la correspondance sur la chaîne de caractères dans son intégralité, alors que lookingAt() le fait partiellement.

La méthode matches() retourne true si l'intégralité d'une chaîne vérifie exactement le motif.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher("Java");
    boolean trouve = matcher.matches();
    System.out.println(trouve);
    matcher = pattern.matcher("Java utilise les regex");
    trouve = matcher.matches();
    System.out.println(trouve);
  }
}

Résultat :
true
false

Le comportement de la méthode lookAt() est différent car la correspondance ne se fait pas sur l'intégralité de la chaîne de caractères.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher("Java");
    boolean trouve = matcher.lookingAt();
    System.out.println(trouve);
    matcher = pattern.matcher("Java utilise les regex");
    trouve = matcher.lookingAt();
    System.out.println(trouve);
  }
}

Résultat :
true
true

La méthode find() tente de trouver la correspondance suivante du motif.

La recherche de correspondance commence au début de la région du Matcher ou, si une invocation précédente de la méthode a réussi et que le comparateur n'a pas été réinitialisé depuis, au premier caractère qui n'a pas été trouvé par la correspondance précédente.

Elle renvoie true si une correspondance est trouvée : dans ce cas, des informations supplémentaires peuvent être obtenues grâce aux méthodes start(), end() et group().

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    Pattern pattern = Pattern.compile("Java");   
    Matcher matcher = pattern.matcher("Java Java Java");
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
Nb occurrences = 3

Une surcharge de le méthode find() qui attend en paramètre un entier de type int permet de préciser l'index de début de la recherche de correspondance.

Par convention, la méthode start() renvoie l'index de début et la méthode end() renvoie l'index de fin plus un qui correspond à l'index suivant.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher("Java");
  
    while (matcher.find()) {
      System.out.printf("Correspondance \"%s\" debut a l'" +
          "index %d, fin a l'index %d.%n",
          matcher.group(),
          matcher.start(),
          matcher.end());
    }
  }
}

Résultat :
Correspondance "Java" debut a l'index 0, fin a l'index 4

Dans l'exemple ci-dessus, bien que la chaîne de caractères contienne quatre caractères, l'index de début est à 0 et l'index de fin est à 4.

Lors des correspondances suivantes, il y a un chevauchement ; l'index de début de la correspondance suivante est le même que l'index de fin de la correspondance précédente.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher("JavaJavaJava");
  
    while (matcher.find()) {
      System.out.printf("Correspondance \"%s\" debut a l'" +
          "index %d, fin a l'index %d%n",
          matcher.group(),
          matcher.start(),
          matcher.end());
    }
  }
}

Résultat :
Correspondance "Java" debut a l'index 0, fin a l'index 4
Correspondance "Java" debut a l'index 4, fin a l'index 8
Correspondance "Java" debut a l'index 8, fin a l'index 12

 

 

23.1.2.3. Les méthodes concernant les groupes

Plusieurs méthodes permettent d'obtenir des informations relatives aux groupes capturés durant la correspondance d'un motif sur une chaîne de caractères.

Méthode

Rôle

String group()

Renvoyer la sous-chaîne correspondant au groupe capturé précédent lors de la correspondance

String group(int group)

Renvoyer la sous-chaîne correspondant au groupe capturé dont le numéro est fourni en paramètre

String group(String name)

Renvoyer la sous-chaîne correspondant au groupe capturé dont le nom est fourni en paramètre lors de la correspondance (depuis java 1.7)

int groupCount()

Renvoyer le nombre de groupes capturés lors de la correspondance

int start(int group)

Renvoyer l'index de début de la sous-chaîne capturée par le groupe dont le numéro est fourni en paramètre

int end(int group)

Renvoyer l'index de fin de la sous-chaîne capturée par le groupe dont le numéro est fourni en paramètre

 

23.1.2.4. Les méthodes de remplacement

Plusieurs méthodes permettent d'effectuer des remplacements.

Méthode

Rôle

Matcher appendReplacement(StringBuffer sb, String replacement)

Exécuter une étape intermédiaire d'ajout et remplacement 

StringBuffer appendTail(StringBuffer sb)

Exécuter l'étape finale d'ajout et remplacement 

String replaceAll(String replacement)

Remplacer toutes les sous-chaines de la séquence d'entrée qui correspond au motif par la chaîne de remplacement fournie

String replaceFirst(String replacement)

Remplacer la première sous-chaine de la séquence d'entrée qui correspond au motif par la chaîne de remplacement fournie

static String quoteReplacement(String s)

Renvoyer une chaîne littérale utilisable comme remplacement pour la chaîne spécifiée. Cette méthode produit une chaîne qui fonctionnera comme un remplacement littéral de la chaîne fournie en paramètre.

La chaîne produite correspondra à la séquence de caractères fournie traitée comme une séquence littérale. Les caractères backslash « \ » et dollar « $ » n'ont pas de signification particulière.

 

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String resultat = "";
    Pattern pattern = Pattern.compile("euros");
 
    Matcher matcher = pattern.matcher("Le prix est de 100 euros TTC");
 
    try {
      resultat = matcher.replaceAll("$");
    } catch (Exception e) {
      System.out.println("Exception : " + e.getClass().getCanonicalName() 
        + " "  + e.getMessage());
    }
    resultat = matcher.replaceAll(matcher.quoteReplacement("$"));
    System.out.println("Chaine de remplacement echappée : "+matcher.quoteReplacement("$"));
    System.out.println(resultat);
  }
}

Résultat :
Exception : java.lang.IllegalArgumentException Illegal group reference: group index is missing
Chaine de remplacement echappée : \$
Le prix est de 100 $ TTC

 

23.1.3. La classe PatternSyntaxException

La classe PatternSyntaxException est une exception unchecked qui indique qu'une expression régulière contient une erreur de syntaxe.

Elle possède plusieurs méthodes permettant d'obtenir des informations sur l'erreur de syntaxe :

Méthode

Rôle

String getDescription()

Renvoyer la description de l'erreur

int getIndex()

Renvoyer l'index de l'erreur

String getPattern()

Renvoyer l'expression régulière qui contient l'erreur de syntaxe

String getMessage()

Renvoyer un message multilignes qui décrit l'erreur de syntaxe


Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
 
public class RegEx {
 
  public static void main(String[] args) {
    try {
    Pattern pattern = Pattern.compile("\\x");
    } catch (PatternSyntaxException pse) {
      pse.printStackTrace();
      System.out.println("\nDescription :");
      System.out.println(pse.getDescription());
      System.out.println("\nIndex :");
      System.out.println(pse.getIndex());
      System.out.println("\nPattern :");
      System.out.println(pse.getPattern());
      System.out.println("\nMessage :");
      System.out.println(pse.getMessage());
    }
  }
}

Résultat :
java.util.regex.PatternSyntaxException: Illegal hexadecimal escape sequence near index 2
\x
        at java.base/java.util.regex.Pattern.error(Pattern.java:2038)
        at java.base/java.util.regex.Pattern.x(Pattern.java:3378)
        at java.base/java.util.regex.Pattern.escape(Pattern.java:2608)
        at java.base/java.util.regex.Pattern.atom(Pattern.java:2296)
        at java.base/java.util.regex.Pattern.sequence(Pattern.java:2169)
        at java.base/java.util.regex.Pattern.expr(Pattern.java:2079)
        at java.base/java.util.regex.Pattern.compile(Pattern.java:1793)
        at java.base/java.util.regex.Pattern.<init>(Pattern.java:1440)
        at java.base/java.util.regex.Pattern.compile(Pattern.java:1079)
        at fr.jmdoudoux.dej.regex.RegEx.main(RegEx.java:10)
 
Description :
Illegal hexadecimal escape sequence
 
Index :
2
 
Pattern :
\x
 
Message :
Illegal hexadecimal escape sequence near index 2
\x

 

23.2. La mise en oeuvre des expressions régulières

Le premier paramètre de la méthode compile() de la classe Pattern est le motif qui décrit l'expression régulière.

La plus grande complexité lors de la mise en oeuvre des expressions régulières est de définir le motif qui correspond aux besoins.

La syntaxe des motifs des expressions régulières est proche de celle utilisée en Perl mais il y a quelques différences.

Les expressions régulières peuvent être testées et même analysées grâce à un débogueur en ligne sur le site https://regex101.com/. Il permet d'utiliser plusieurs syntaxes dont celle de l'API de Java.

La syntaxe permet d'utiliser différents concepts selon les besoins :

Les expressions régulières peuvent contenir des métacaractères qui ont des rôles particuliers dans l'expression du motif.

Syntaxe Rôle
^

Correspond à un début de la chaîne

$

Correspond à une fin de la chaîne

.

Correspond à n'importe quel caractère sauf par défaut une terminaison de ligne

[abc]

Correspond à un des caractères entre la paire de crochets

[^abc]

Correspond à n'importe quel caractère sauf ceux entre la paire de crochets

\A

Correspond au début de la chaîne dans son intégralité

\z

Correspond à la fin de la chaîne dans son intégralité

\Z

Correspond à la fin de la chaîne dans son intégralité à l'exception de la terminaison de ligne finale. Si une terminaison de ligne est présente, la correspondance se fait sur le caractère précédent.

re*

Correspond à une répétition 0 à n fois de l'excodession précédente

re+

Correspond à une répétition 1 à n fois de l'excodession précédente

re?

Correspond à une répétition 0 à 1 fois de l'expression précédente

re{ n}

Correspond à une répétition exactement n fois de l'expression précédente

re{ n,}

Correspond à une répétition au moins n fois de l'expression précédente

re{ n, m}

Correspond à une répétition d'au moins n fois et au plus m fois de l'expression précédente

a|b

Correspond à l'une ou l'autre des expressions à gauche et à droite. Ici les caractères a ou b

(re)

Définir un groupe de capture grâce à l'expression et permettre d'obtenir le texte correspondant

\w

Correspond à des caractères dans des mots

\W

Correspond à des caractères qui ne sont pas dans des mots

\s

Correspond à des caractères d'espacement. Equivalent à [\t\n\r\f]

\S

Correspond à des caractères qui ne sont pas des caractères d'espacement

\d

Correspond à des chiffres. Equivalent à [0-9]

\D

Correspond à n'importe quoi sauf des chiffres

\G

Correspond à l'endroit où la dernière correspondance s'est terminée

\b

Correspond aux caractères qui sont un début ou une fin de mot

\B

Correspond aux caractères qui ne sont pas un début ou une fin de mot

\n

Correspond à une nouvelle ligne

\t

Correspond à une tabulation

\Q

Débuter la considération des caractères comme des littéraux suivants jusqu'à \E

\E

Finir la considération des caractères comme des littéraux initiée par \Q


La regex est appliquée sur la chaîne de caractère de gauche à droite à la recherche de correspondance. Une fois qu'un caractère de la chaîne a été utilisé dans une correspondance, il ne peut pas être réutilisé pour une autre.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "xyxyxyxyx";
    Pattern pattern = Pattern.compile("xyx");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(),
        matcher.end())+", debut="+matcher.start());
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
} 

Résultat :
1 : xyx, debut=0
2 : xyx, debut=4
Nb occurrences = 2

 

23.2.1. L'échappement de caractères

Les caractères backslash dans les chaînes de caractères littérales dans le code source Java sont interprétées comme des échappements de caractères. Il est donc nécessaire de doubler les backslash dans les chaînes de caractères littérales qui contiennent des expressions régulières afin de les protéger de l'interprétation par le compilateur Java.

Les expressions régulières en Java utilise dans leur syntaxe un certain nombre de caractères considérés comme spéciaux car ils ont une signification particulière :

\.[]{}()*+?^$|

Le caractère backslash « \ » est le caractère d'échappement dans les chaînes de caractères en Java. Il doit lui-même être échappé par un caractère backslash qui le précède.

Les métacaractères qui débutent par un backslash doivent donc être exprimés dans une chaîne de caractères en Java par deux backslashs.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    System.out.println(Pattern.matches("\\d", "0"));
  }
}

Résultat :
true

Le caractère backslash est aussi le caractère d'échappement dans une expression régulière. La chaîne littérale "\b", par exemple, correspond à un simple caractère de retour arrière lorsqu'il est interprété comme une expression régulière, tandis que "\\b" correspond à une limite de correspondance de mot. La chaîne de caractères littérale "\(java\)" est illégale et provoque une erreur de compilation. Pour indiquer la chaîne "(java)", il faut utiliser la chaîne de caractères littérale "\\(java\\)".

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    System.out.println(Pattern.matches("\\{\\}", "{}"));
  }
}

Résultat :
true

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    System.out.println(Pattern.matches("\\(10\\)", "(10)"));
  }
}

Résultat :
true

Pour exprimer un backslash littéral dans une expression régulière, il faut donc utiliser quatre backslashs qui se suivent.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    System.out.println(Pattern.matches("\\\\", "\\"));
  }
}

Résultat :
true

Une exception de type PatternSyntaxException est levée lors de l'utilisation d'un backslash avant tout caractère alphabétique qui ne désigne pas une construction échappée. Ces échappements sont réservés pour de futures extensions de la syntaxe des expressions régulières.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    System.out.println(Pattern.matches("\\j", "10"));
  }
}

Résultat :
Exception in thread "main" java.util.regex.PatternSyntaxException: Illegal/unsupported 
escape sequence near index 1
\j
 ^
        at java.base/java.util.regex.Pattern.error(Pattern.java:2038)
        at java.base/java.util.regex.Pattern.escape(Pattern.java:2618)
        at java.base/java.util.regex.Pattern.atom(Pattern.java:2296)
        at java.base/java.util.regex.Pattern.sequence(Pattern.java:2169)
        at java.base/java.util.regex.Pattern.expr(Pattern.java:2079)
        at java.base/java.util.regex.Pattern.compile(Pattern.java:1793)
        at java.base/java.util.regex.Pattern.<init>(Pattern.java:1440)
        at java.base/java.util.regex.Pattern.compile(Pattern.java:1079)
        at java.base/java.util.regex.Pattern.matches(Pattern.java:1184)
        at fr.jmdoudoux.dej.regex.RegEx.main(RegEx.java:9)

 

23.2.2. Les terminaisons de ligne (Line terminators)

Une terminaison de ligne est une séquence d'un ou deux caractères qui désigne la fin d'une ligne dans un ensemble de caractères. Plusieurs terminaisons de ligne sont reconnues par défaut dans les expressions régulières Java :

La séquence \R représente toute séquence de saut de ligne Unicode : elle est équivalente à
\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

Par défaut, l'expression « . » correspond à un caractère quelconque excepté une terminaison de ligne. Pour modifier ce comportement, il est possible d'utiliser le mode DOTALL.

Par défaut, les expressions ^ and $ ignore les terminaisons de ligne et correspondent uniquement au début et à la fin de l'ensemble de la chaîne de caractères.

Si le mode UNIX_LINES est activé, la seule terminaison de ligne reconnue est le caractère newline.

Si le mode MULTILINE est activé, ^ correspond au début de la chaîne d'entrée et après tout terminaison de ligne, sauf à la fin de la chaîne d'entrée. En mode MULTILINE, $ est utilisé juste avant une fin de ligne ou la fin de la chaîne d'entrée.

 

23.2.3. Une simple chaîne littérale

L'expression régulière la plus simple consiste simplement en une simple chaîne de caractères littérale.

Les caractères peuvent être exprimés dans le motif de l'expression régulière de différentes manières :

Séquence

Rôle

X

Le caractère x

\\

Le caractère backslash

\0n

Le caractère correspond correspondant à la valeur octale 0n (0 <= n <= 7)

\0nn

Le caractère correspond correspondant à la valeur octale 0nn (0 <= n <= 7)

\0mnn

Le caractère correspond correspondant à la valeur octale 0mnn (0 <= m <= 3, 0 <= n <= 7)

\xhh

Le caractère correspond correspondant à la valeur hexadécimale 0xhh

\uhhhh

Le caractère correspond correspondant à la valeur hexadécimale 0xhhhh

\x{h...h}

Le caractère correspond correspondant à la valeur hexadécimale 0xh...h (Character.MIN_CODE_POINT <= 0xh...h <= Character.MAX_CODE_POINT)

\t

Le caractère tabulation ('\u0009')

\n

Le caractère newline (line feed) ('\u000A')

\r

Le caractère retour chariot (carriage-return) ('\u000D')

\f

Le caractère form-feed ('\u000C')

\a

Le caractère alert (bell) ('\u0007')

\e

Le caractère escape ('\u001B')


Lorsqu'une expression régulière est une simple chaîne de caractères, elle peut correspondre à zéro ou plusieurs fois selon le contenu de la chaîne de caractère sur laquelle elle est appliquée.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher("Java");
    System.out.println(matcher.find());
  }
}

Résultat :
true

Il est possible d'itérer sur l'invocation de la méthode find() pour trouver plusieurs occurrences.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher("JavaJava JavaJava");
    int nbOcc = 0;
    while (matcher.find()) {
        nbOcc++;
    }
    System.out.println("Nb occurences = " + nbOcc);
  }
}

Résultat :
Nb occurences = 4

 

23.2.4. Les métacaractères (Meta Characters)

Pour exprimer des besoins plus complexes dans le motif, il est possible d'utiliser des métacaractères. Ce sont des caractères qui ont une signification particulière et peuvent être combinés.

Les métacaractères modifient la manière dont un motif est utilisé pour la correspondance.

Plusieurs métacaractères sont utilisables dans les motifs :

<([{\^-=$!|]})?*+.>

Métacaractère

Rôle

.

Un seul caractère, n'importe lequel sauf une terminaison de ligne

*

0, 1 ou plusieurs caractères

?

0 ou une fois le motif qui précède

()

Groupe de capture

[]

Ensemble de caractères

{}

Quantificateur

\

Déspécialise le métacaractère qu'il précède

^

Début de ligne ou négation

$

Fin de ligne

|

Ou logique entre deux motifs

+

Une ou plusieurs fois le motif qui précède


Il y a deux manières de déspécialiser un métacaractère :

 

23.2.5. Les classes de caractères (Character Classes)

Une classe de caractères définit un ensemble de caractères.

Les classes de caractères peuvent être incluses à l'intérieur d'autres classes de caractères.

Elles peuvent aussi être composées grâce à plusieurs opérateurs qui peuvent être combinés :

 

23.2.5.1. Les ensembles de caractères

Un ensemble de caractères est une classe de caractères qui se définit en utilisant une paire de crochets.

Une syntaxe particulière peut être utilisée à l'intérieure de la paire de crochets pour définir l'ensemble de caractères :

Expression

Rôle

[abc] 

Définir un ensemble de caractères : ceux fournis dans la paire de crochets

[^abc] 

Définir un ensemble de caractères : ceux non fournis dans la paire de crochets

[0-9]   

Définir un ensemble de caractères : ceux définis par la plage dont les bornes sont fournies à gauche et à droite du caractère moins

[a-zA-Z] 

Définir un ensemble de caractères : l'union des deux plages


Attention : les métacaractères «^» et «$» ont un rôle différent entre une paire de crochets lors de la définition de classes de caractères.

Il est possible de définir un ensemble de caractères définis par ceux fournis.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "java";
    Pattern pattern = Pattern.compile("[ajv]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : j
2 : a
3 : v
4 : a
Nb occurrences = 4

Il est possible de définir un ensemble de caractères définis par l'inverse de ceux fournis.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "java";
    Pattern pattern = Pattern.compile("[^perl]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : j
2 : a
3 : v
4 : a
Nb occurrences = 4

Il est possible de définir une plage de caractères.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java";
    Pattern pattern = Pattern.compile("[A-Z]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
Nb occurrences = 1

Une plage de caractères peut inclure des lettres majuscules, des chiffres et tous les caractères Unicode entre "0" et "Z".

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String texte = "Java : 1 <= 2";
    Pattern pattern = Pattern.compile("[0-Z]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
2 : :
3 : 1
4 : <
5 : =
6 : 2
Nb occurrences = 6

 

23.2.5.2. L'opérateur OR

C'est l'opérateur par défaut utilisé entre les caractères précisés dans la paire de crochets.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "test";
    Pattern pattern = Pattern.compile("[est]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : t
2 : e
3 : s
4 : t
Nb occurrences = 4

Un ensemble de caractères peut être combinés avec d'autres expressions pour former le motif.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "java Java";
    Pattern pattern = Pattern.compile("[jJ]ava");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : java
2 : Java
Nb occurrences = 2

 

23.2.5.3. L'opérateur NOR

Cet opérateur permet de définir l'ensemble inverse des caractères précisés entre la paire de crochet. Il utilise le caractère ^ qui doit précéder l'ensemble de caractères.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java";
    Pattern pattern = Pattern.compile("[^Jav]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
Nb occurrences = 0

 

23.2.5.4. Une plage

Il est possible de définir une plage de caractères en séparant les deux bornes avec un caractère « - ».

Exemple avec des lettres minuscules

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java";
    Pattern pattern = Pattern.compile("[a-z]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : a
2 : v
3 : a
Nb occurrences = 3

Exemple avec des lettres majuscules

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "JaVa";
    Pattern pattern = Pattern.compile("[A-Z]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
2 : V
Nb occurrences = 2

Il est possible de combiner des plages. Par exemple, pour définir un ensemble de lettres minuscules et majuscules :

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "JaVa";
    Pattern pattern = Pattern.compile("[a-zA-Z]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
2 : a
3 : V
4 : a
Nb occurrences = 4

Exemple : une plage de chiffres

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java 19";
    Pattern pattern = Pattern.compile("[0-9]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : 1
2 : 9
Nb occurrences = 2

 

23.2.5.5. L'opérateur union

L'opérateur union permet de combiner 2 ou plusieurs classes de caractères.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java";
    Pattern pattern = Pattern.compile("[a-c[I-L]]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
2 : a
3 : a
Nb occurrences = 3

 

23.2.5.6. L'opérateur intersection

L'opérateur intersection permet de définir l'intersection 2 ou plusieurs classes de caractères : cela revient à prendre les caractères en commun dans les ensembles. Il utilise les caractères && qui doivent être utilisés entre deux ensembles de caractères.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "java";
    Pattern pattern = Pattern.compile("[a-z&&[aeiou]]");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : a
2 : a
Nb occurrences = 2

 

23.2.5.7. La soustraction de classe de caractères (Subtraction Class)

Il est possible d'utiliser une soustraction pour exclure une classe de caractères. Elle se fait en utilisant une combinaison des opérateurs intersection et NOR.

Par exemple, l'exemple ci-dessous fait une correspondance avec les nombres impairs :

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "123456789";
    Pattern pattern = Pattern.compile("[0-9&&[^02468]]");
    Matcher matcher = pattern.matcher(texte);
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = "+nbOcc);
  }
}

Résultat :
1
3
5
7
9
Nb occurrences = 5

 

23.2.5.8. Les classes de caractères prédéfinies (Predefined Character Classes)

L'API pour les expressions régulières de Java propose un ensemble de classes de caractères prédéfinies. Ces classes de caractères prédéfinies sont des raccourcis pour des besoins courants.

Ces métacaractères ont la même première lettre que leur représentation en anglais, par exemple, d pour digit (chiffre), s pour space (caractère d'espacement), w pour word (mot), ... Les classes de caractères en majuscules définissent le contraire.

Plusieurs classes de caractères prédéfinies sont proposées par l'API :

Classe

Description

\d

Un chiffre. Equivalent à : [0-9]

\D

Tout sauf un chiffre. Equivalent à : [^0-9]

\h

Un caractère d'espacement horizontal. Equivalent à : [ \t\xA0\u1680\u180e\u2000-\u200a\u202f\u205f\u3000]

\H

Tout sauf un caractère d'espacement horizontal. Equivalent à : [^\h]

\s

Un caractère d'espacement. Equivalent à : [ \t\n\x0B\f\r]

\S

Tout sauf un caractère d'espacement. Equivalent à : [^\s]

\v

Un caractère d'espacement vertical. Equivalent à : [\n\x0B\f\r\x85\u2028\u2029]

\V

Tout sauf un caractère d'espacement vertical. Equivalent à : [^\v]

\w

Un caractère alphanumérique. Equivalent à : [a-zA-Z_0-9]

\W

Tout sauf un caractère alphanumérique. Equivalent à : [^\w]

.

N'importe quel caractère sauf par défaut une terminaison de ligne


La plupart de ces classes de caractères débutent par un caractère « \ » qui doit être échappé avec un autre caractère « \ » comme dans toute chaîne de caractères en Java.

Le métacaractère « . » permet de désigner n'importe quel caractère excepté une terminaison de ligne.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile(".a.a");
    Matcher matcher = pattern.matcher("Java");
    System.out.println(matcher.find());
  }
}

Résultat :
true

La recherche de la correspondance peut être effectuée plusieurs fois.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile(".");
    Matcher matcher = pattern.matcher("Java");
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
    }
    System.out.println("Nb occurrences = "+nbOcc);
  }
}

Résultat :
Nb occurrences = 4

La classe \d correspond à un chiffre.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java 19";
    Pattern pattern = Pattern.compile("\\d");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : 1
2 : 9
Nb occurrences = 2

La classe \D correspond à tout sauf un chiffre.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java 19";
    Pattern pattern = Pattern.compile("\\D");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
2 : a
3 : v
4 : a
5 :  
Nb occurrences = 5

La classe \s correspond à un caractère d'espacement.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java 19";
    Pattern pattern = Pattern.compile("\\s");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 :  
Nb occurrences = 1

La classe \S correspond à tout sauf un caractère d'espacement.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java 19";
    Pattern pattern = Pattern.compile("\\S");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
2 : a
3 : v
4 : a
5 : 1
6 : 9
Nb occurrences = 6

La classe \w correspond un caractère alphanumérique.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java 19";
    Pattern pattern = Pattern.compile("\\w");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : J
2 : a
3 : v
4 : a
5 : 1
6 : 9
Nb occurrences = 6

La classe \W correspond à tout sauf un caractère alphanumérique.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java 19 n'est pas LTS";
    Pattern pattern = Pattern.compile("\\W");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 :  
2 :  
3 : '
4 :  
5 :  
Nb occurrences = 5

 

23.2.5.9. Les classes de caractères POSIX

Des classes de caractères POSIX (uniquement US-ASCII) sont proposées :

Classes

Correspondance

\p{ASCII}

Tous les caractères ASCII : [\x00-\x7F]

\p{Alnum}

Un caractère alphanumérique : [\p{Alpha}\p{Digit}]

\p{Alpha}

Un caractère alphabétique : [\p{Lower}\p{Upper}]

\p{Blank}

Un espace ou une tabulation : [ \t]

\p{Cntrl}

Un caractère de contrôle : [\x00-\x1F\x7F]

\p{Digit}

Un chiffre : [0-9]

\p{Graph}

Un caractère visible : [\p{Alnum}\p{Punct}]

\p{Lower}

Un caractère alphabétique minuscule : [a-z]

\p{Print}

Un caractère affichable : [\p{Graph}\x20]

\p{Punct}

Un caractère de ponctuation : !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

\p{Space}

Un caractère d'espacement : [ \t\n\x0B\f\r]

\p{Upper}

Un caractère alphabétique minuscule : [A-Z]

\p{XDigit}

Un chiffre hexadécimal : [0-9a-fA-F]


Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "Remise de 10% !";
    Pattern pattern = Pattern.compile("\\p{Punct}");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : %
2 : !
Nb occurrences = 2

 

23.2.5.10. Les classes de java.lang.Character

Plusieurs classes correspondantes à l'invocation de méthodes de la classe java.lang.Character sont proposées :

Classe

Correspondance

\p{javaLowerCase}

Equivalent à java.lang.Character.isLowerCase()

\p{javaUpperCase}

Equivalent à java.lang.Character.isUpperCase()

\p{javaWhitespace}

Equivalent à java.lang.Character.isWhitespace()

\p{javaMirrored}

Equivalent à java.lang.Character.isMirrored()


Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "(123)[abc]";
    Pattern pattern = Pattern.compile("\\p{javaMirrored}");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : (
2 : )
3 : [
4 : ]
Nb occurrences = 4

 

23.2.5.11. Les classes pour Unicode

Des classes sont proposées pour les scripts, les blocs, les catégories et les propriétés binaires Unicode.

Les scripts, blocs, catégories et propriétés binaires Unicode sont utilisés avec \p et \P. \p{prop} correspond à la propriété prop, tandis que \P{prop} ne correspond pas à la propriété.

Les scripts peuvent être précisés de plusieurs manières :

Les noms de scripts utilisables sont ceux définis et valides pour la méthode forName() de l'énumération UnicodeScript.

Les blocs peuvent être précisés de plusieurs manières :

Les noms de scripts utilisables sont ceux définis et valides pour la méthode forName() de l'énumération UnicodeBlock.

Les catégories peuvent être précisées de plusieurs manières :

Les catégories utilisables sont celles définies dans le standard Unicode dont la version est supportée par la classe Character.

Un caractère Unicode peut également être représenté dans une expression régulière en utilisant sa notation hexadécimale (valeur du code point hexadécimal) en utilisant la séquence \x{...}.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
  
  public static void main(String[] args) {
    String texte = "aBcD";
    Pattern pattern = Pattern.compile("\\p{Lu}");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
 
  }
}

Résultat :
1 : B
2 : D
Nb occurrences = 2

Les propriétés binaires (Binary properties) sont spécifiées avec le préfixe « Is », comme par exemple IsAlphabetic. Les propriétés binaires prises en charge par la classe Pattern sont :

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
  
  public static void main(String[] args) {
    String texte = "aBcD";
    Pattern pattern = Pattern.compile("\\p{IsUpperCase}");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
 
  }
}

Résultat :
1 : B
2 : D
Nb occurrences = 2

 

23.2.6. Les quantificateurs (Quantifiers)

Un quantificateur permet de préciser le nombre d'occurrences d'un motif d'une expression régulière lors de la correspondance.

Les quantificateurs sont utilisés dans une expression régulière en utilisant les métacaractères ?, *, + et {}.

Motif

Rôle

*

Répétition zéro ou plusieurs fois, raccourci pour {0,}

Exemple :

A* : zéro ou plusieurs lettres A

.* : zéro ou plusieurs lettres quelconque

+

Répétition une ou plusieurs fois, raccourci pour {1,}

Exemple :

A+ : une ou plusieurs lettres A

?

Répétition une ou plusieurs fois, raccourci pour {0,1}

Exemple :

A ? : zéro ou une fois la lettre A

{X}

Répétition exactement X fois

Exemple :

\d{3} : trois chiffres

.{5} : 5 caractères quelconques

{X,Y}

Répétition entre X et Y fois

Exemple :

\d{1,3} : entre un et trois chiffres


Les quantificateurs peuvent fonctionner selon trois modes avec des comportements différents :

Quantificateurs

Rôle

Avide
(Greedy)

Réticent
(Reluctant)

Possessif
(Possessive)

X?

X??

X?+

Zéro ou une fois

X*

X*?

X*+

Zéro ou plusieurs fois

X+

X+?

X++

Une ou plusieurs fois

X{n}

X{n}?

X{n}+

Exactement n fois

X{n,}

X{n,}?

X{n,}+

Au moins n fois

X{n, m}

X{n, m}?

X{n, m}+

Au moins n fois et jusqu'à m fois


Pour faire correspondre un motif zéro ou une fois, il faut utiliser le quantificateur « ? »

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w?");
    rechercherCorrespondance(pattern, "");
    rechercherCorrespondance(pattern, "a");
    rechercherCorrespondance(pattern, "aa");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
: correspondance trouvee
a : correspondance trouvee
aa : aucune correspondance

Alternativement, il est possible d'utiliser un quantificateur avec une paire d'accolades.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w{0,1}");
    rechercherCorrespondance(pattern, "");
    rechercherCorrespondance(pattern, "a");
    rechercherCorrespondance(pattern, "aa");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
: correspondance trouvee
a : correspondance trouvee
aa : aucune correspondance

Pour faire correspondre un motif zéro ou plusieurs fois, il faut utiliser le quantificateur « * »

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w*");
    rechercherCorrespondance(pattern, "");
    rechercherCorrespondance(pattern, "a");
    rechercherCorrespondance(pattern, "ab");
    rechercherCorrespondance(pattern, "abc");
    rechercherCorrespondance(pattern, " ");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
: correspondance trouvee
a : correspondance trouvee
ab : correspondance trouvee
abc : correspondance trouvee
  : aucune correspondance

Alternativement, il est possible d'utiliser un quantificateur avec une paire d'accolades.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w{0,}");
    rechercherCorrespondance(pattern, "");
    rechercherCorrespondance(pattern, "a");
    rechercherCorrespondance(pattern, "ab");
    rechercherCorrespondance(pattern, "abc");
    rechercherCorrespondance(pattern, " ");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
: correspondance trouvee
a : correspondance trouvee
ab : correspondance trouvee
abc : correspondance trouvee
  : aucune correspondance

Remarque : ce quantificateur peut trouver une correspondance avec un motif sur une chaîne de caractères vide en entrée puisque le motif peut être trouvé zéro ou plusieurs fois.

Pour faire correspondre un motif une ou plusieurs fois, il faut utiliser le quantificateur « + »

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w+");
    rechercherCorrespondance(pattern, "");
    rechercherCorrespondance(pattern, "a");
    rechercherCorrespondance(pattern, "ab");
    rechercherCorrespondance(pattern, "abc");
    rechercherCorrespondance(pattern, " ");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
: aucune correspondance
a : correspondance trouvee
ab : correspondance trouvee
abc : correspondance trouvee
  : aucune correspondance

Alternativement, il est possible d'utiliser un quantificateur avec une paire d'accolades.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w{1,}");
    rechercherCorrespondance(pattern, "");
    rechercherCorrespondance(pattern, "a");
    rechercherCorrespondance(pattern, "ab");
    rechercherCorrespondance(pattern, "abc");
    rechercherCorrespondance(pattern, " ");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
: aucune correspondance
a : correspondance trouvee
ab : correspondance trouvee
abc : correspondance trouvee
  : aucune correspondance

Pour faire correspondre un motif un nombre exact de fois, il faut utiliser un quantificateur avec une paire d'accolades.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w{3}");
    rechercherCorrespondance(pattern, "ab");
    rechercherCorrespondance(pattern, "abc");
    rechercherCorrespondance(pattern, "abcd");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
ab : aucune correspondance
abc : correspondance trouvee
abcd : aucune correspondance

Pour faire correspondre un motif un nombre de fois précisé dans une plage de valeur, il faut utiliser un quantificateur avec une paire d'accolades.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("\\w{2,3}");
    rechercherCorrespondance(pattern, "a");
    rechercherCorrespondance(pattern, "ab");
    rechercherCorrespondance(pattern, "abc");
    rechercherCorrespondance(pattern, "abcd");
  }
 
  private static void rechercherCorrespondance(Pattern pattern, String texte) {
    Matcher matcher = pattern.matcher(texte);
    if (matcher.matches()) {
      System.out.println(texte + " : correspondance trouvee");
    } else {
      System.out.println(texte + " : aucune correspondance");
    }
  }
}

Résultat :
a : aucune correspondance
ab : correspondance trouvee
abc : correspondance trouvee
abcd : aucune correspondance

L'utilisation d'un des trois mode (avide, réticent, possessif) a des conséquences sur la recherche de la correspondance. Par exemple avec motif ".*Java" qui désigne zéro ou plusieurs caractères suivi de « Java ».

Le premier exemple utilise un quantificateur avide.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "xJavaxxxxxxJava";
    Pattern pattern = Pattern.compile(".*Java");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : xJavaxxxxxxJava
Nb occurrences = 1

Comme le quantificateur est avide, le motif .* consomme l'intégralité de la chaîne de caractères en entrée. À ce moment, la correspondance échoue car les quatre derniers caractères « Java » sont consommés. Le comparateur du Matcher recule caractère par caractère jusqu'à ce qu'il trouve « Java », ce qui fait trouver la correspondance et arrêter la recherche. Ainsi une seule correspondance est trouvée.

Le second exemple utilise un quantificateur réticent.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "xJavaxxxxxxJava";
    Pattern pattern = Pattern.compile(".*?Java");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : xJava
2 : xxxxxxJava
Nb occurrences = 2

Le quantificateur réticent commence par vérifier si la chaîne commence par « Java ». Comme ce n'est pas le cas, il consomme les caractères pour trouver une correspondance et répète l'opération jusqu'à la fin de la chaîne de caractères. Ainsi deux correspondances sont trouvées.

Le troisième exemple utilise un quantificateur possessif.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "xJavaxxxxxxJava";
    Pattern pattern = Pattern.compile(".*+Java");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
Nb occurrences = 0

Avec le quantificateur possessif, toute la chaîne correspond à .*+ et il n'y a donc pas de correspondance avec « Java ». Aucune correspondance n'est donc trouvée.

Un quantificateur possessif est intéressant pour trouver une correspondance sans jamais reculer, ce qui le rend dans ce cas plus performant qu'un quantificateur avide.

 

23.2.7. Les groupes de capture (capturing groups)

Il est possible de regrouper des parties d'une expression régulière. Dans le motif, un groupe est défini entourant les éléments qui composent le groupe d'une paire de parenthèses.

Les groupes de captures permettent de traiter plusieurs caractères comme une seule unité sous la forme d'un groupe. Par exemple, (java) crée un groupe contenant les caractères "j", "a", "v" et "a".

Les groupes de capture sont ainsi nommés parce que, pendant une correspondance, chaque sous-séquence de la séquence d'entrée qui correspond à un tel groupe est conservée en mémoire. La sous-séquence capturée peut être utilisée ultérieurement dans l'expression, par le biais d'une référence arrière (back reference), et peut également être récupérée dans le Matcher une fois l'opération de correspondance terminée.

L'entrée capturée associée à un groupe est toujours la sous-séquence que le groupe a fait correspondre le plus récemment. Si un groupe est évalué une seconde fois en raison de la quantification, sa valeur précédemment capturée, le cas échéant, sera conservée si la seconde évaluation échoue. Par exemple, si la chaîne de caractères "xyx" est comparée à l'expression (x(y) ?)+, la valeur "y" sera attribuée au groupe deux. Toutes les entrées capturées sont rejetées au début de chaque correspondance.

 

23.2.7.1. La syntaxe de définition de groupes

Un groupe de capture est défini en utilisant une paire de parenthèses.

Exemple :
    Pattern pattern = Pattern.compile("(Java)");

Un groupe peut être imbriqué dans un autre groupe.

Exemple :
    Pattern pattern = Pattern.compile("(J((av)(a)))");

Il est possible d'assigner une quantification à un groupe.

Exemple :
    Pattern pattern = Pattern.compile("(Java){2}");

 

23.2.7.2. L'utilisation des méthodes de la classe Matcher pour les groupes

Une fois l'application de l'expression régulière, la classe Matcher propose plusieurs méthodes qui permettent d'obtenir des informations sur les groupes capturés :

Méthode

Rôle

int groupCount()

Renvoyer le nombre de groupes capturés

String group()

Renvoyer la valeur du groupe capturé par la précédente correspondance

String group(int)

Renvoyer la valeur du groupe capturé dont le numéro est fourni en paramètre

int start(int)

Renvoyer l'index du début du groupe dont le numéro est fourni en paramètre

int end(int) 

Renvoyer l'index de la fin du groupe dont le numéro est fourni en paramètre


La surcharge de la méthode group() qui attend en paramètre entier renvoie la sous-chaîne capturée lors la précédente recherche de correspondance pour le groupe dont le numéro est fourni en paramètre.

Pour un Matcher m, une chaîne d'entrée c, et un indice de groupe g, les expressions m.group(g) et c.substring(m.start(g ), m.end(g)) sont équivalentes.

Si la correspondance réussie mais que le groupe spécifié ne correspond à aucune partie de la chaîne d'entrée, null est renvoyé.

La surcharge de la méthode group() qui attend en paramètre une chaîne de caractères renvoie la sous-chaîne capturée lors la précédente recherche de correspondance pour le groupe dont le nom est fourni en paramètre.

Si la correspondance est réussie mais que le groupe spécifié ne correspond à aucune partie de la séquence d'entrée, null est renvoyé.

Elle peut lever deux exceptions :

Certains groupes, par exemple (a*), peuvent correspondre à une chaîne vide. Ces deux surcharges renvoient une chaîne vide lorsqu'un tel groupe correspond à la chaîne vide de l'entrée.

La méthode groupCount() de la classe Matcher renvoie le nombre total de groupe contenu dans une expression.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String MOTIF_IPV4 = "^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\."
        + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\."
        + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\."
        + "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$";
    Pattern pattern = Pattern.compile(MOTIF_IPV4);
    Matcher matcher = pattern.matcher("127.0.0.1");
    if (matcher.matches()) {
      for (int i = 1; i <= matcher.groupCount(); i++) {
        System.out.println("Groupe " + i + " :" + matcher.group(i));
      }
    } else {
      System.out.println("Correspondance non trouvée");
    }
  }
}

Résultat :
Groupe 1 :127
Groupe 2 :0
Groupe 3 :0
Groupe 4 :1

 

23.2.7.3. La numérotation des groupes

Un nombre est associé à chaque groupe trouvé : il est possible d'utiliser ce nombre pour désigner le groupe correspondant.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "Java";
    Pattern pattern = Pattern.compile("(Java)");
    Matcher matcher = pattern.matcher(texte);
 
    System.out.println("Nb groupes = " + matcher.groupCount());
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); ++i) {
        System.out.println("groupe " + i + " :" + matcher.group(i));
      }
    }
  }
}

Résultat :
Nb groupes = 1
groupe 0 : Java
groupe 1 : Java

Le groupe 0 correspond toujours à l'intégralité de l'expression régulière.

Les groupes de capture sont numérotés en comptant leurs parenthèses ouvrantes de gauche à droite.

Par exemple, dans l'expression ((X)(Y(Z)) il y a quatre groupes :

  1. ((X)(Y(Z)))
  2. (X)
  3. (Y(Z))
  4. (Z)
Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "Java";
    Pattern pattern = Pattern.compile("(J((av)(a)))");
    Matcher matcher = pattern.matcher(texte);
 
    System.out.println("Nb groupes = " + matcher.groupCount());
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); ++i) {
        System.out.println("groupe " + i + " : " + matcher.group(i));
      }
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Nb groupes = 4
groupe 0 : Java
groupe 1 : Java
groupe 2 : ava
groupe 3 : av
groupe 4 : a

L'exemple ci-dessus définit 4 groupes. Ils sont numérotés de gauche à droite selon l'ordre de leur parenthèse ouvrante.

 

23.2.7.4. Le nommage d'un groupe

Depuis Java 7, il est possible d'affecter un nom à un groupe pour permettre d'y faire référence à travers de ce nom.

Le nom d'un groupe est composé d'un ensemble de caractères qui peuvent être :

La première lettre doit obligatoirement être une lettre.

Pour nommer un groupe, il faut utiliser la syntaxe :

(?<nom>sous-motif)

Un groupe qui possède un nom possède aussi un numéro attribué automatiquement.

Exemple ( code Java 7 ) :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "Le prix est de 315 euros";
    Pattern pattern = Pattern.compile("(?<libelle>\\D*)(?<montant>\\d*)(?<monnaie>\\D*)");
 
    Matcher matcher = pattern.matcher(chaine);
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); i++) {
        System.out.println("Groupe " + i + " : " + matcher.group(i));
      }
    } else {
      System.out.println("Correspondance non trouvée");
    }
  }
}


Résultat :
Groupe 0 : Le prix est de 315 euros
Groupe 1 : Le prix est de 
Groupe 2 : 315
Groupe 3 :  euros

La surcharge de la méthode group() de la classe Matcher qui attend en paramètre une chaîne de caractères permet d'obtenir la valeur capturée du groupe dont le nom est fourni en paramètre.

Exemple ( code Java 7 ) :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "Le prix est de 315 euros";
    Pattern pattern = Pattern.compile("(?<libelle>\\D*)(?<montant>\\d*)(?<monnaie>\\D*)");
 
    Matcher matcher = pattern.matcher(chaine);
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); i++) {
        System.out.println("Groupe " + i + " : " + matcher.group(i));
      }
    } else {
      System.out.println("Correspondance non trouvée");
    }
  }
}


Résultat :
Groupe 0 : Le prix est de 315 euros
Groupe 1 : Le prix est de 
Groupe 2 : 315
Groupe 3 :  euros

Une exception de type IllegalArgumentException est levée si le nom du groupe fourni en paramètre n'existe pas dans l'expression régulière.

Exemple ( code Java 7 ) :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "Le prix est de 315 euros";
    Pattern pattern = Pattern.compile("(?<libelle>\\D*)(?<montant>\\d*)(?<monnaie>\\D*)");
 
    Matcher matcher = pattern.matcher(chaine);
    if (matcher.matches()) {
      System.out.println("Groupe libelle : " + matcher.group("libelle"));
      System.out.println("Groupe montant : " + matcher.group("montant"));
      System.out.println("Groupe monnaie : " + matcher.group("devise"));
      
    } else {
      System.out.println("Correspondance non trouvée");
    }
  }
}


Résultat :
Groupe libelle : Le prix est de 
Groupe montant : 315
Exception in thread "main" java.lang.IllegalArgumentException: No group with name <devise>
        at java.base/java.util.regex.Matcher.getMatchedGroupIndex(Matcher.java:1802)
        at java.base/java.util.regex.Matcher.group(Matcher.java:680)
        at fr.jmdoudoux.dej.regex.RegEx.main(RegEx.java:17)

Il n'est pas possible s'associer le même nom à deux groupes dans une même expression régulière. Dans ce cas, une exception de type PatternSyntaxException est levée à la compilation de l'expression.

Exemple ( code Java 7 ) :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "Le prix est de 315 euros";
    Pattern pattern = Pattern.compile("(?<libelle>\\D*)(?<montant>\\d*)(?<libelle>\\D*)");
 
    Matcher matcher = pattern.matcher(chaine);
    if (matcher.matches()) {
      System.out.println("Groupe libelle : " + matcher.group("libelle"));
      System.out.println("Groupe montant : " + matcher.group("montant"));
      System.out.println("Groupe monnaie : " + matcher.group("monnaie"));
      
    } else {
      System.out.println("Correspondance non trouvée");
    }
  }
}


Résultat :
Exception in thread "main" java.util.regex.PatternSyntaxException: Named capturing group
<libelle> is already defined near index 40
(?<libelle>\D*)(?<montant>\d*)(?<libelle>\D*)
                                        ^
        at java.base/java.util.regex.Pattern.error(Pattern.java:2038)
        at java.base/java.util.regex.Pattern.group0(Pattern.java:3002)
        at java.base/java.util.regex.Pattern.sequence(Pattern.java:2134)
        at java.base/java.util.regex.Pattern.expr(Pattern.java:2079)
        at java.base/java.util.regex.Pattern.compile(Pattern.java:1793)
        at java.base/java.util.regex.Pattern.<init>(Pattern.java:1440)
        at java.base/java.util.regex.Pattern.compile(Pattern.java:1079)
        at fr.jmdoudoux.dej.regex.RegEx.main(RegEx.java:11)

 

23.2.7.5. Les références arrières (back reference)

Il est possible de faire référence à un groupe dans le motif en utilisant son numéro ou éventuellement son nom si le groupe en possède un.

Il est possible de faire référence au contenu dans un groupe en utilisant une référence arrière en utilisant le caractère \ suivi du numéro du groupe concerné.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "JavaJava";
    Pattern pattern = Pattern.compile("(Java)\\1");
    Matcher matcher = pattern.matcher(texte);
 
    System.out.println("Nb groupes = " + matcher.groupCount());
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); ++i) {
        System.out.println("groupe " + i + " : " + matcher.group(i));
      }
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Nb groupes = 1
groupe 0 : JavaJava
groupe 1 : Java

La correspondance se fait sur la même valeur que celle du groupe capturé précisé.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "JavaFX";
    Pattern pattern = Pattern.compile("(Java)\\1");
    Matcher matcher = pattern.matcher(texte);
 
    System.out.println("Nb groupes = " + matcher.groupCount());
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); ++i) {
        System.out.println("groupe " + i + " : " + matcher.group(i));
      }
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Nb groupes = 1
Aucune correspondance

Il est possible de définir un autre groupe identique à un autre en utilisant une référence arrière (back reference) en utilisant son numéro comme référence.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "JavaJava";
    Pattern pattern = Pattern.compile("(Java)(\\1)");
    Matcher matcher = pattern.matcher(texte);
 
    System.out.println("Nb groupes = " + matcher.groupCount());
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); ++i) {
        System.out.println("groupe " + i + " : " + matcher.group(i));
      }
    }
  }
}

Résultat :
Nb groupes = 2
groupe 0 : JavaJava
groupe 1 : Java
groupe 2 : Java

Il est aussi possible d'utiliser une référence sur le nom du groupe en utilisant la syntaxe :

\k<nom>
Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
  
  public static void main(String[] args) {
    String texte = "JavaJava";
    Pattern pattern = Pattern.compile("(?<cafe>Java)\\k<cafe>");
    Matcher matcher = pattern.matcher(texte);
 
    System.out.println("Nb groupes = " + matcher.groupCount());
    if (matcher.matches()) {
      for (int i = 0; i <= matcher.groupCount(); ++i) {
        System.out.println("groupe " + i + " : " + matcher.group(i));
      }
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Nb groupes = 1
groupe 0 : JavaJava
groupe 1 : Java

 

23.2.7.6. Les groupes non capturants

Il est parfois utile de créer un groupe dont on n'a pas besoin de la capture de la correspondance.

La syntaxe d'un groupe non capturant est de la forme :

(?:motif)
Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    traiter("Ja(va)?", "Java");
    traiter("Ja(va)?", "Ja");
    traiter("Ja(?:va)?", "Java");
    traiter("Ja(?:va)?", "Ja");
  }
 
  public static void traiter(String regex, String chaine) {
    System.out.printf("%nRegex : %s, chaine : %s%n", regex, chaine);
    Matcher matcher = Pattern.compile(regex)
        .matcher(chaine);
    while (matcher.find()) {
      System.out.printf("Groupe 0, debut=%s, fin=%s, correspondance='%s'%n",
        matcher.start(), matcher.end(), matcher.group());
      for (int i = 1; i <= matcher.groupCount(); i++) {
        System.out.printf("Groupe %s, debut=%s, fin=%s, correspondance='%s'%n",
          i, matcher.start(i), matcher.end(i), matcher.group(i));
      }
    }
  }
}

Résultat :
 
Regex : Ja(va)?, chaine : Java
Groupe 0, debut=0, fin=4, correspondance='Java'
Groupe 1, debut=2, fin=4, correspondance='va'
 
Regex : Ja(va)?, chaine : Ja
Groupe 0, debut=0, fin=2, correspondance='Ja'
Groupe 1, debut=-1, fin=-1, correspondance='null'
 
Regex : Ja(?:va)?, chaine : Java
Groupe 0, debut=0, fin=4, correspondance='Java'
 
Regex : Ja(?:va)?, chaine : Ja
Groupe 0, debut=0, fin=2, correspondance='Ja'

Dans l'exemple ci-dessus, on veut que la séquence « va » de « Java » soit facultative en utilisant le quantificateur « ? ». Il est nécessaire de regrouper « va » à l'aide de parenthèses pour définir un groupe qui sera facultatif. Si on utilise « va ? », seule la lettre « a » sera facultative, pas la séquence « va ». Dans ce cas, il n'est pas utile de capturer le groupe car il ne sera pas réutilisé dans le motif. C'est la raison pour laquelle un groupe non capturant est utilisé : (?:va)?

 

23.2.7.7. Les groupes atomiques

Les groupes atomiques ne font pas marche arrière (backtrack) une fois qu'une correspondance est réussie. Les groupes atomiques rejettent/oublient les parties suivantes du groupe une fois qu'une correspondance a été trouvée.

La syntaxe des groupes atomiques est de la forme :

<?>motif>

Les groupes atomiques ne sont pas capturant mais peuvent avoir des groupes de capture imbriqués.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    traiter("p(at|a)te", "patte");
    traiter("p(at|a)te", "pate");
    traiter("p(ot|at)e", "pate");
 
    traiter("p(?>at|a)te", "patte");
    traiter("p(?>at|a)te", "pate");
    traiter("p(?>ot|at)e", "pate");
  }
 
  public static void traiter(String regex, String chaine) {
    System.out.printf("%nRegex : %s, chaine : %s%n", regex, chaine);
    Matcher matcher = Pattern.compile(regex)
        .matcher(chaine);
    if (matcher.find()) {
      do {
        System.out.printf("Groupe 0, debut=%s, fin=%s, correspondance='%s'%n",
          matcher.start(), matcher.end(), matcher.group());
        for (int i = 1; i <= matcher.groupCount(); i++) {
          System.out.printf("Groupe %s, debut=%s, fin=%s, correspondance='%s'%n", i, 
            matcher.start(i), matcher.end(i), matcher.group(i));
        }
      } while (matcher.find());
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
 
Regex : p(at|a)te, chaine : patte
Groupe 0, debut=0, fin=5, correspondance='patte'
Groupe 1, debut=1, fin=3, correspondance='at'
 
Regex : p(at|a)te, chaine : pate
Groupe 0, debut=0, fin=4, correspondance='pate'
Groupe 1, debut=1, fin=2, correspondance='a'
 
Regex : p(ot|at)e, chaine : pate
Groupe 0, debut=0, fin=4, correspondance='pate'
Groupe 1, debut=1, fin=3, correspondance='at'
 
Regex : p(?>at|a)te, chaine : patte
Groupe 0, debut=0, fin=5, correspondance='patte'
 
Regex : p(?>at|a)te, chaine : pate
Aucune correspondance
 
Regex : p(?>ot|at)e, chaine : pate
Groupe 0, debut=0, fin=4, correspondance='pate'

Lors de l'utilisation d'un groupe atomique s'il n'y a pas de correspondance c'est parce que le moteur ne revient pas en arrière pour les groupes atomiques. Le premier « p » de la chaîne d'entrée correspond à la partie littérale « p » de l'expression. Ensuite, « at » de la chaîne d'entrée correspond à la partie « at » dans le groupe. Le reste de la chaîne d'entrée « e » ne correspond pas au reste de l'expression régulière « te ». Le moteur ne revient pas en arrière parce que le groupe est atomique pour essayer une autre combinaison. Cette situation n'est valable que si le moteur a une correspondance avec la combinaison actuelle mais n'a pas de correspondance globale. En d'autres termes, le groupe atomique ne revient pas en arrière après la première correspondance de combinaison, même s'il existe une correspondance globale.

Si la première combinaison ne correspond pas parce que « ot » de la chaîne d'entrée ne satisfait pas la première combinaison du groupe, le moteur fait marche arrière pour vérifier la seconde combinaison, même si le groupe est atomique.

 

23.2.8. Les assertions lookaround

Parfois, il est difficile de faire correspondre une chaîne de caractères avec une expression régulière. Par exemple, il se peut que nous ne sachions pas exactement ce que nous voulons faire correspondre, mais nous savons qui vient directement avant ou ce qui manque après. Dans ces cas, il est possible d'utiliser les assertions lookaround. Ces expressions sont appelées assertions car elles indiquent seulement si quelque chose est une correspondance ou non, mais ne sont pas incluses dans le résultat.

Lookahead et lookbehind, collectivement appelés "lookaround", sont des assertions de longueur zéro, tout comme les métacaractères de début et de fin de ligne ou de début et de fin de mot. Les assertions ne consomment pas de caractères de la chaîne, mais affirment seulement si une correspondance est possible ou non.

Les fonctionnalités Lookaround permettent de créer des expressions régulières qu'il serait impossible de créer sans elles, ou qui deviendraient très fastidieuses sans elles.

Il existe quatre types d'assertions utilisables dans les expressions régulières.

Syntaxe

Nom

Rôle

(?=foo)

Lookahead

Affirme que ce qui suit immédiatement la position actuelle dans la chaîne est foo

(?!foo)

Negative Lookahead

Affirme que ce qui suit immédiatement la position actuelle dans la chaîne n'est pas foo

(?<=foo)

Lookbehind

Affirme que ce qui précède immédiatement la position actuelle dans la chaîne est foo

(?<!foo)

Negative Lookbehind

Affirme que ce qui précède immédiatement la position actuelle dans la chaîne n'est pas foo


Les assertions lookahead et lookbehind ne consomment aucun caractère de la chaîne d'entrée. Cela signifie qu'après la parenthèse de fermeture du lookahead ou du lookbehind, le moteur de regex reste à l'endroit même de la chaîne où il a commencé à chercher. À partir de cette position, le moteur peut recommencer à faire correspondre des caractères.

Le fait que le lookaround soit de longueur nulle le rend automatiquement atomique. Dès que la condition de la zone de recherche est satisfaite, le moteur de regex oublie tout ce qui se trouve à l'intérieur du lookaround.

Il est toutefois possible d'utiliser des groupes de capture à l'intérieur du lookaround.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "5x5 55x55 155x55 551x55";
    Pattern pattern = Pattern.compile("(?=(\\d+))\\w+\\1");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : 5x5
2 : 55x55
3 : 55x55
Nb occurrences = 3

 

23.2.8.1. Les assertions Lookahead

Les assertions Lookaheads sont des assertions de longueur zéro, ce qui signifie qu'elles ne sont pas incluses dans la correspondance. Elles affirment seulement si la portion immédiate qui précède la portion courante d'une chaîne d'entrée donnée est appropriée pour une correspondance ou non.

La syntaxe générale d'une assertion lookahead est de la forme : elle commence par une parenthèse ouvrante suivi d'un point d'interrogation « ( ? » suivie d'un métacaractère « = » ou « ! » suivi d'une regex d'affirmation (toutes les expressions regex sont autorisées) suivie de parenthèses fermantes « ) ».

Il existe deux types d'assertions lookahead :

Chacune d'entre elles a deux syntaxes :

Les deux appliquent la condition d'assertion en amont de leur position.

Tous les motifs valides peuvent être utilisés dans un lookahead.

 

23.2.8.1.1. Positive Lookahead

Une assertion positive lookahead est utile pour faire correspondre quelque chose suivi d'autre chose.

La syntaxe d'un positive lookahead utilise une paire de parenthèses, contenant un point d'interrogation et un signe égal suivi du motif.

Exemple :

o(?=u)

La correspondance se fait sur «o» suivi de «u», avec «u» qui ne fait pas partie de la correspondance.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "oui";
    Pattern pattern = Pattern.compile("o(?=u)");
 
    Matcher matcher = pattern.matcher(chaine);
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + chaine.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : o
Nb occurrences = 1

Dans l'exemple ci-dessus, le moteur applique l'expression régulière «o(?=u)» à la chaîne «oui». Le premier élément de la regex est le littéral «o». Le moteur parcourt la chaîne jusqu'à ce que le caractère «o» soit trouvé dans la chaîne. L'élément suivant dans la regex est le lookahead. Le moteur prend note qu'il est à l'intérieur d'une construction lookahead maintenant, et commence à faire correspondre la regex à l'intérieur du lookahead. L'élément suivant de la regex est le u à l'intérieur du lookahead. Le caractère suivant est un «u». Il y a correspondance. Le moteur avance au caractère suivant : «i». Cependant, il le fait avec la regex à l'intérieur du lookahead. Le moteur note le succès, et rejette la correspondance de l'expression régulière. Cela amène le moteur à reculer dans la chaîne jusqu'à u. La correspondance se fait uniquement sur le caractère «o» qui est bien suivi d'un caractère «u».

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "oui";
    Pattern pattern = Pattern.compile("o(?=u)i");
 
    Matcher matcher = pattern.matcher(chaine);
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + chaine.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
Nb occurrences = 0

Dans l'exemple ci-dessus, le moteur applique l'expression régulière «o(?=u)i» à la chaîne «oui».

Le positive lookahead est suivi d'un autre élément dans la regex. Encore une fois, «o» correspond à «o» suivi d'un «u» qui correspond à «u». Encore une fois, la correspondance du lookahead est écartée, donc le moteur recule de «i» dans la chaîne à «u». Le lookahead a réussi, donc le moteur continue avec «i». Mais «i» ne correspond pas à «u». Donc cette tentative de correspondance échoue.

La regex o(?=u)i ne peut jamais correspondre à quoi que ce soit. Elle essaie de faire correspondre «u» et «i» à la même position. S'il y a un «u» immédiatement après le «o», le lookahead réussit mais «i» ne correspond pas à «u». S'il y a autre chose qu'un «u» immédiatement après le «o», le lookahead échoue.

Pour modifier ce comportement, il faut ajouter le métacaractère «.» après le lookahead pour que le moteur reconsomme le caractère «u».

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "oui";
    Pattern pattern = Pattern.compile("o(?=u).i");
 
    Matcher matcher = pattern.matcher(chaine);
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + chaine.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : oui
Nb occurrences = 1

Dans l'exemple ci-dessus, la correspondance réussie car «oui» est bien composé du caractère «o» suivi du caractère «u» et du caractère «i». La correspondance obtenue est «oui».

Si l'assertion contient des groupes de capture, ces groupes seront capturés normalement et des back references vers eux sont utilisables même en dehors du lookahead.

Une assertion lookahead elle-même n'est pas un groupe de capture. Elle n'est pas incluse dans le calcul de la numérotation des back references. Si l'on veut stocker la correspondance du motif à l'intérieur d'un lookahead, il faut mettre des parenthèses de capture autour du motif à l'intérieur du loohahead (?=(motif)).

Positive lookahead après la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "10 pour 315 euros", "20 pour 263 dollars" };
    Pattern pattern = Pattern.compile("\\d+(?= euros)");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
10 pour 315 euros
1 : 315
Nb occurrences = 1
20 pour 263 dollars
Nb occurrences = 0

L'expression régulière "\\d+(?= euros)" correspond à des chiffres suivis immédiatement de la chaîne de caractères " euros ".

Positive lookahead avant la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "10 pour 315 euros", "20 pour 263 dollars" };
    Pattern pattern = Pattern.compile("(?=\\d+ euros)\\d+");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
10 pour 315 euros
1 : 315
Nb occurrences = 1
20 pour 263 dollars
Nb occurrences = 0

Le positive lookahead (?=\d+ euros) affirme qu'à la position actuelle dans la chaîne, ce qui suit est des chiffres puis les caractères " euros". Si l'assertion réussit, le moteur fait correspondre les chiffres avec \d+.

Avec une assertion avant la correspondance, il est nécessaire de répéter le motif de la correspondance recherchée. Dans l'exemple ci-dessus, dans la regex «?=\d+ euros)\d+», le motif \d+ est répété deux fois, une fois à l'intérieur du lookahead et une fois à l'extérieur. C'est nécessaire quel que soit l'assertion fournie au moteur, il faut aussi lui ajouter la correspondance recherchée. Il ne faut répéter que la partie pour laquelle la correspondance est recherchée. Si ce n'est pas le cas alors il n'y aura simplement pas de correspondance trouvée car l'assertion échouera.

Pour simplifier la répétition, il est possible de définir un groupe capture dans l'assertion et d'utiliser une back reference.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "10 pour 315 euros", "20 pour 263 dollars" };
    Pattern pattern = Pattern.compile("(?=(\\d+) euros)\\1");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
10 pour 315 euros
1 : 315
Nb occurrences = 1
20 pour 263 dollars
Nb occurrences = 0

Le motif utilisé permet d'obtenir le même résultat que \d+(?= euros) utilisé précédemment, mais il est moins efficace car \d+ est évalué deux fois. Une meilleure utilisation de l'assertion avant la correspondance est de valider plusieurs conditions.

 

23.2.8.1.2. Negative Lookahead

Une assertion negative lookahead est à utiliser lorsque l'on veut faire correspondre quelque chose qui n'est pas suivi par autre chose. Ce comportement ne peut pas être mis en ouvre avec des classes de caractères. Un negative lookahead fournit la solution.

La syntaxe d'un negative lookahead utilise une paire de parenthèses, contenant un point d'interrogation et d'un point d'exclamation suivi du motif.

Exemple :

a(?!z)

Negative Lookahead après la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "10 pour 315 euros", "20 pour 263 dollars" };
    Pattern pattern = Pattern.compile("\\d+(?!\\d+| euros)");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
10 pour 315 euros
1 : 10
Nb occurrences = 1
20 pour 263 dollars
1 : 20
2 : 263
Nb occurrences = 2

Le lookahead (?!\d+| euros) affirme qu'à la position actuelle dans la chaîne, ce qui suit immédiatement n'est ni un chiffre ni les caractères " euros". Si l'assertion réussit, le moteur fait correspondre les chiffres avec \d+.

Negative Lookahead avant la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "10 pour 315 euros", "20 pour 263 dollars" };
    Pattern pattern = Pattern.compile("(?!\\d+ euros)\\d+");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
10 pour 315 euros
1 : 10
Nb occurrences = 1
20 pour 263 dollars
1 : 20
2 : 263
Nb occurrences = 2

Le lookahead négatif (?!\d+ euros) affirme qu'à la position actuelle dans la chaîne, ce qui suit n'est pas des chiffres mais les caractères " euros". Si l'assertion réussit, le moteur fait correspondre les chiffres avec \d+.

Le motif utilisé permet d'obtenir le même résultat que \d+(?!\d+| euros) utilisé précédemment, mais il est moins efficace car \d+ est évalué deux fois. Une meilleure utilisation de l'assertion avant la correspondance est de valider plusieurs conditions.

 

23.2.8.2. Les assertions lookbehind

Une assertion lookbehind a le même effet que lookahead, mais fonctionne à l'envers.

Il indique au moteur regex de reculer temporairement dans la chaîne de caractères, pour vérifier si le texte contenu dans le lookbehind peut être trouvé à cet endroit.

Lookbehind est une autre assertion de longueur nulle, tout comme les assertions Lookahead. Leur assertion est validée seulement si la portion immédiate derrière la portion courante d'une chaîne d'entrée donnée est appropriée pour une correspondance ou non.

Il y a deux types d'assertions lookbehind :

Chacun possède deux syntaxes :

Les deux appliquent la condition d'assertion derrière leur position.

 

23.2.8.2.1. Positive lookBehind

Une assertion positive lookbehind est utile pour faire correspondre quelque chose précédé d'autre chose.

La syntaxe d'un positive lookbehind utilise une paire de parenthèses, contenant un point d'interrogation, un inférieur et un signe égal suivi du motif.

Exemple :

(?<=X)

Positive lookbehind avant la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "Code : 1234", "PIN : 5678" };
    Pattern pattern = Pattern.compile("(?<=Code : )\\d{4}");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
Code : 1234
1 : 1234
Nb occurrences = 1
PIN : 5678
Nb occurrences = 0

Le lookbehind positif (?<=Code : ) affirme qu'à la position actuelle dans la chaîne, ce qui précède est composé des caractères "Code : ". Si l'assertion réussit, le moteur fait correspondre les 4 chiffres avec \d{4}.

Positive lookbehind après la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "Code : 1234", "PIN : 5678" };
    Pattern pattern = Pattern.compile("\\d{4}(?<=Code : \\d{4})");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
Code : 1234
1 : 1234
Nb occurrences = 1
PIN : 5678
Nb occurrences = 0

"\d{4}" correspond à 4 chiffres. Le lookbehind "(?<=Code : \d{4})" affirme qu'à cette position dans la chaîne, ce qui précède immédiatement est la séquence "Code : " puis 4 chiffres.

Le motif utilisé permet d'obtenir le même résultat que "(?<=Code : )\d{4}" utilisé précédemment, mais il est moins efficace car "\d{4}" est évalué deux fois.

 

23.2.8.2.2. Negative Lookbehind

Une assertion negative lookbehind est utile pour faire correspondre quelque chose qui ne soit pas précédé d'autre chose.

Le moteur de regex traite un negative lookbehind de la même manière qu'un positive lookbehind avant la correspondance sauf qu'il applique une négation sur l'assertion.

La syntaxe d'un negative lookbehind utilise une paire de parenthèses, contenant un point d'interrogation, un inférieur et un point d'exclamation suivi du motif.

Exemple :

(?<!X)

Negative lookbehind avant la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "Code : 1234", "PIN : 5678" };
    Pattern pattern = Pattern.compile("(?<!Code : )\\d{4}");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
Code : 1234
Nb occurrences = 0
PIN : 5678
1 : 5678
Nb occurrences = 1

Le lookbehind négatif "(?<!Code : )" affirme qu'à la position actuelle dans la chaîne, ce qui précède n'est pas composé des caractères "Code : ". Si l'assertion réussit, le moteur fait correspondre quatre chiffres avec "\d{4}".

Negative lookbehind après la correspondance

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaines[] = { "Code : 1234", "PIN : 5678" };
    Pattern pattern = Pattern.compile("\\d{4}(?<!Code : \\d{4})");
 
    for (int i = 0; i < chaines.length; i++) {
      Matcher matcher = pattern.matcher(chaines[i]);
      System.out.println(chaines[i]);
      int nbOcc = 0;
      while (matcher.find()) {
        nbOcc++;
        System.out.println(nbOcc + " : " + chaines[i].substring(matcher.start(),
          matcher.end()));
      }
      System.out.println("Nb occurrences = " + nbOcc);
    }
  }
}

Résultat :
Code : 1234
Nb occurrences = 0
PIN : 5678
1 : 5678
Nb occurrences = 1

"\d{4}" correspond à 4 chiffes. Le lookbehind négatif "(?<!Code : \d{4})" affirme qu'à cette position dans la chaîne, ce qui précède immédiatement n'est pas les caractères " Code : " puis 4 chiffres.

Le motif utilisé permet d'obtenir le même résultat que "\d{4}(?<!Code : )" utilisé précédemment, mais il est moins efficace car "\d{4}" est évalué deux fois.

 

23.2.8.2.3. Les limitations des assertions Lookbehind en Java

La plupart des moteurs ne supportent pas toutes les expressions dans un Lookbehind. La raison est que le moteur doit être capable de déterminer le nombre de caractères à reculer avant de vérifier l'expression fournie dans le lookbehind .

Lors de l'évaluation du lookbehind, le moteur d'expression régulière détermine la longueur de l'expression régulière contenue dans le lookbehind, recule d'autant de caractères dans la chaîne du sujet, puis applique l'expression régulière contenue dans le lookbehind de gauche à droite, comme il le ferait avec une expression régulière normale.

Java autorise tout sauf les quantificateurs et les métacaractères '+' et '*' dans une assertion lookbehind qui présentent des limitations dans certains cas.

Dans des cas comme "[0-9]*", ces quantificateurs fonctionnent, mais ils ne fonctionnent pas dans des cas comme "x[0-9]*" (lorsque l'expression est bornée à gauche).

Exemple :
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    valider("a2 456b 456789c 45d ef", "(?<=[0-9]+)[a-z]");
  }
 
  private static void valider(String chaine, String regex) {
    Pattern pattern = Pattern.compile(regex);
 
    Matcher matcher = pattern.matcher(chaine);
    System.out.println(chaine);
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + chaine.substring(matcher.start(),
        matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
C:\java>java RegEx
a2 456b 456789c 45d ef
1 : b
2 : c
3 : d
Nb occurrences = 3

Le moteur de regex de Java permet l'utilisation de répétition finie dans un lookbehind. Il est possible d'utiliser le métacaractère «?» et une quantification avec le paramètre max spécifié. Java détermine les longueurs minimales et maximales possibles du lookbehind.

Le moteur va tenter successivement tenter d'appliquer l'expression du lookbehind de gauche à droite en reculant de la longueur minimale vers la longueur maximale tant que la correspondance n'est pas trouvée. Cette recherche peut nuire aux performances notamment si la longueur maximale est grande.

Certains bugs présents en Java 4 et 5 dans l'exploitation d'un lookbehind sont corrigés en Java 6.

Une des limitations concernent une quantification supérieure sans limite sur un motif qui est lui-même précédé par au moins un autre motif.

Exemple :
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    valider("a2 456b 456789c 45d ef", "(?<=4[0-9]{3,})[a-z]");
  }
 
  // ...
}

Résultat :
C:\java>java -version
openjdk version "1.7.0_75"
OpenJDK Runtime Environment (build 1.7.0_75-b13)
OpenJDK Client VM (build 24.75-b04, mixed mode)
 
C:\java>javac RegEx.java
 
C:\java>java RegEx
Exception in thread "main" java.util.regex.PatternSyntaxException: Look-behind group
 does not have an obvious maximum length near index 13
(?<=4[0-9]{3,})[a-z]
             ^
        at java.util.regex.Pattern.error(Pattern.java:1924)
        at java.util.regex.Pattern.group0(Pattern.java:2812)
        at java.util.regex.Pattern.sequence(Pattern.java:2018)
        at java.util.regex.Pattern.expr(Pattern.java:1964)
        at java.util.regex.Pattern.compile(Pattern.java:1665)
        at java.util.regex.Pattern.<init>(Pattern.java:1337)
        at java.util.regex.Pattern.compile(Pattern.java:1022)
        at RegEx.valider(RegEx.java:12)
        at RegEx.main(RegEx.java:8)

Cela a été corrigé en Java 13.

Résultat :
C:\java>java -version
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment (build 13+33)
OpenJDK 64-Bit Server VM (build 13+33, mixed mode, sharing)
 
C:\java>java RegEx
a2 456b 456789c 45d ef
1 : c
Nb occurrences = 1

Une autre limitation concerne l'utilisation des métacaractères « + » et « * » pour la répétition d'un motif lui-même précédé d'au moins un autre motif.

Exemple :
public class RegEx {
 
  public static void main(String[] args) {
 
    valider("a2 456b 456789c 45d ef", "(?<=4[0-9]+)[a-z]");
  }
  
  // ...
}

Résultat :
C:\java>java -version
openjdk version "1.7.0_75"
OpenJDK Runtime Environment (build 1.7.0_75-b13)
OpenJDK Client VM (build 24.75-b04, mixed mode)
 
C:\java>java RegEx
Exception in thread "main" java.util.regex.PatternSyntaxException: Look-behind group
 does not have an obvious maximum length near index 10
(?<=4[0-9]*)[a-z]
          ^
        at java.util.regex.Pattern.error(Pattern.java:1924)
        at java.util.regex.Pattern.group0(Pattern.java:2812)
        at java.util.regex.Pattern.sequence(Pattern.java:2018)
        at java.util.regex.Pattern.expr(Pattern.java:1964)
        at java.util.regex.Pattern.compile(Pattern.java:1665)
        at java.util.regex.Pattern.<init>(Pattern.java:1337)
        at java.util.regex.Pattern.compile(Pattern.java:1022)
        at RegEx.valider(RegEx.java:12)
        at RegEx.main(RegEx.java:8)

Une possibilité pour contourner cette limitation est d'utiliser un quantificateur avec une limite supérieure à définir pour ne pas être trop élevée afin de ne pas pénaliser les performances.

Exemple :
public class RegEx {
 
  public static void main(String[] args) {
    valider("a2 456b 456789c 45d ef", "(?<=4[0-9]{1,10})[a-z]");
  }
 
  // ... 
}

Résultat :
C:\java>java -version
openjdk version "1.7.0_75"
OpenJDK Runtime Environment (build 1.7.0_75-b13)
OpenJDK Client VM (build 24.75-b04, mixed mode)
 
C:\java>java RegEx
a2 456b 456789c 45d ef
1 : b
2 : c
3 : d
Nb occurrences = 3

Cette limitation a été réglée en Java 9.

Exemple :
public class RegEx {
 
  public static void main(String[] args) {
    valider("a2 456b 456789c 45d ef", "(?<=4[0-9]*)[a-z]");
  }
 
  // ...
}

Résultat :
C:\java>java -version
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)
 
C:\java>java RegEx
a2 456b 456789c 45d ef
1 : b
2 : c
3 : d
Nb occurrences = 3

Pour des raisons de performance, il est tout de même conseillé d'utiliser à la place un quantificateur avec une limite supérieure à définir pour ne pas être trop élevée afin de ne pas pénaliser les performances.

 

23.2.9. Les limites de correspondance (Boundary Matchers)

L'API RegEx de Java prend également en charge les limites de correspondance. Cela permet de définir où la correspondance doit être trouvée dans la chaîne en entrée car les limites de correspondance permettent de préciser où débute et où fini la recherche d'un motif.

Séquence

Rôle

^

Début de ligne

$

Fin de ligne

\b

Extrémité de mot

\B

Extrémité de non-mot

\A

Début de la séquence en entrée

\G

Fin de l'occurrence précédente

\Z

Fin de la séquence, sauf le caractère final

\z

Fin de la séquence en entrée


Le métacaractère ^ indique le début du texte.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java supporte les regex";
    Pattern pattern = Pattern.compile("^Java");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
Nb occurrences = 1

Une correspondance est trouvée car la chaîne débute par «Java».

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Le langage Java supporte les regex";
    Pattern pattern = Pattern.compile("^Java");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
Nb occurrences = 0

Aucune correspondance n'est trouvée car la chaîne ne débute pas par «Java».

Le métacaractère $ indique la fin du texte.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Les regex sont supportées par Java";
    Pattern pattern = Pattern.compile("Java$");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
Nb occurrences = 1

Une correspondance est trouvée car la chaîne se termine par «Java».

Le métacaractère \b indique une limite de mot. Un espace est considéré comme une limite de mot.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Le langage Java supporte les regex";
    Pattern pattern = Pattern.compile("\\bJava\\b");  
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
Nb occurrences = 1

Un début ou une fin de ligne sont considérés comme une limite de mot.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java supporte les regex";
    Pattern pattern = Pattern.compile("\\bJava\\b");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
Nb occurrences = 1

Aucune correspondance n'est trouvée si le motif n'est pas suivi d'une limite de mot.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "JavaFX est un framework pour interface graphique";
    Pattern pattern = Pattern.compile("\\bJava\\b");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
Nb occurrences = 0

Une correspondance est trouvée en remplaçant la limite de fin par le métacaractère \B qui indique une limite qui ne soit pas un mot.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "JavaFX est un framework pour interface graphique";
    Pattern pattern = Pattern.compile("\\bJava\\B");   
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
Nb occurrences = 1

Le métacaractère \G indique la fin de l'occurrence précédente.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "JavaJava";
    Pattern pattern = Pattern.compile("\\GJava"); 
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
2 : Java
Nb occurrences = 2

Les occurrences doivent se suivrent pour obtenir une correspondance.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "Java Java";
    Pattern pattern = Pattern.compile("\\GJava"); 
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
  }
}

Résultat :
1 : Java
Nb occurrences = 1

Une seule correspondance est trouvée, la première, car la deuxième n'apparaît pas après la première, puisque séparée avec un caractère espace.

 

23.2.10. Les modes utilisables

Différents modes sont utilisables pour influencer la manière dont le motif est interprété.

Pour préciser un ou plusieurs modes, il est possible d'utiliser :

Ces modes sont définies sous la forme de constante de type int dans la classe Pattern :

Constante

Option embarquée

Rôle

CANON_EQ

Activer l'équivalence canonique (canonical equivalence).

Deux caractères seront considérés comme correspondant si leurs décompositions canoniques complètes correspondent. Exemple : l'expression "a\u030A" correspondra à la chaîne "\u00E5".

Ce mode ne possède pas d'option embarquée.

L'utilisation de ce mode peut dégrader les performances.

CASE_INSENSITIVE

(?i)

Activer la correspondance sans tenir compte de la casse.

Par défaut, cette correspondance insensible à la casse suppose que seuls les caractères du jeu de caractères US-ASCII sont mis en correspondance. La correspondance insensible à la casse et sensible à l'Unicode peut être activé en utilisant le mode UNICODE_CASE en conjonction avec ce mode.

L'utilisation de ce mode peut dégrader les performances.

COMMENTS

(?x)

Ignorer les caractères d'espacement et les commentaires dans la regex.

Les commentaires commencent par # et sont ignorés jusqu'à la fin d'une ligne

DOTALL

(?s)

Activer le mode dotall : la correspondance de "." inclut aussi les caractères de terminaison de lignes.

LITERAL

Le motif est traité comme une séquence de caractères littéraux : les métacaractères contenus dans le motif n'ont pas de signification particulière et sont traités comme des caractères ordinaires lors de la recherche de correspondance.

Les modes CASE_INSENSITIVE et UNICODE_CASE conservent leur impact sur la correspondance lorsqu'ils sont utilisés conjointement avec ce mode. Les autres modes sont superflus.

Ce mode ne possède pas d'option embarquée.

(depuis Java 1.5)

MULTILINE

(?m)

Activer le mode multilignes.

Dans ce mode, les métacaractères ^ et $ correspondent juste après ou juste avant respectivement à une fin d'une ligne ou à la fin de la séquence d'entrée.

UNICODE_CASE

(?u)

Activer la gestion des caractères Unicode. Le mode CASE_INSENSITIVE est utilisable conjointement avec ce mode pour ne pas tenir compte de la casse.

L'utilisation de ce mode peut dégrader les performances.

UNICODE_CHARACTER_CLASS

(?U)

Activer la version Unicode des classes de caractères prédéfinies (US-ASCII) et des classes de caractères POSIX pour être conforme avec Unicode Technical Standard #18: Unicode Regular Expression (annexe C).

Ce mode implique le mode UNICODE_CASE.

L'utilisation de ce mode peut dégrader les performances.

(depuis Java 1.7)

UNIX_LINES

(?d)

Activer le mode dans lequel seul la terminaison de ligne Unix \n est utilisé dans le comportement des métacaractère « . », « ^ » et « $ »


Il est possible de combiner plusieurs constantes pour activer plusieurs modes en utilisant l'opérateur | :

Exemple :
    Pattern pattern = Pattern.compile(exp, Pattern.DOTALL | Pattern.UNIX_LINES);

Il est aussi possible de combiner des options embarquées simplement en les concaténant.

Exemple :
    Pattern pattern = Pattern.compile("(?sd).*");

Pattern.CANON_EQ

Ce mode permet d'activer l'équivalence canonique (canonical equivalence). Lorsqu'elle est utilisée, deux caractères seront considérés comme correspondant si leurs décompositions canoniques correspondent.

Par exemple, le caractère accentué Unicode « é », dont le point de code composite est u00E9. Unicode possède également un point de code distinct pour les caractères composants « e » (u0065) et l'accent aigu (u0301). Dans ce cas, le caractère composite u00E9 est équivalent à la séquence des deux caractères u0065 u0301.

Par défaut, la correspondance ne tient pas compte de l'équivalence canonique.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "\u0065\u0301";
    Pattern pattern = Pattern.compile("\u00E9");   
    Matcher matcher = pattern.matcher(texte);
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Aucune correspondance

Le mode CANON_EQ active la prise en compte de l'équivalence canonique lors de la recherche de correspondance.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    
    String texte = "\u0065\u0301";
    Pattern pattern = Pattern.compile("\u00E9", Pattern.CANON_EQ);   
    Matcher matcher = pattern.matcher(texte);
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Ce mode ne peut pas être exprimé sous la forme d'une option embarquée.

 

Pattern.CASE_INSENSITIVE

Par défaut, la correspondance est sensible à la casse.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("java");
    Matcher matcher = pattern.matcher("Java");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Aucune correspondance

Le mode CASE_INSENSITIVE active la correspondance sans tenir compte de la casse.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
    Matcher matcher = pattern.matcher("Java");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Il est aussi possible d'utiliser l'option équivalente en début de l'expression régulière.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("(?i)java");
    Matcher matcher = pattern.matcher("Java");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Pattern.COMMENTS

En Java, il est possible d'utiliser des commentaires dans une expression régulière pour la documenter.

Un commentaire débute par un caractère # dans l'expression régulière.

Par défaut, les commentaires sont interprétés comme faisant partie du motif.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("^Java.* # Commence par Java");
    Matcher matcher = pattern.matcher("Java supporte les regex");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Aucune correspondance

Le mode COMMENTS ignore les caractères d'espacement et les commentaires à la fin dans la regex.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("^Java.* # Commence par Java", Pattern.COMMENTS);
    Matcher matcher = pattern.matcher("Java supporte les regex");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Il est aussi possible d'utiliser l'option équivalente en début de l'expression régulière.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("(?x)^Java.* # Commence par Java");
    Matcher matcher = pattern.matcher("Java supporte les regex");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Pattern.DOTALL

Par défaut, lors de l'utilisation du caractère point "." dans une regex, la correspondance se fait sur un caractère quelconque jusqu'à ce qu'un caractère de nouvelle ligne soit rencontré.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile(".*");
    Matcher matcher = pattern.matcher("Ligne 1\nLigne2\nLigne3");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Aucune correspondance

L'option DOTALL permet de demander que la correspondance de "." inclut aussi les caractères de nouvelle ligne.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile(".*", Pattern.DOTALL);
    Matcher matcher = pattern.matcher("Ligne 1\nLigne2\nLigne3");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Il est aussi possible d'utiliser l'option équivalente en début de l'expression régulière.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("(?s).*");
    Matcher matcher = pattern.matcher("Ligne 1\nLigne2\nLigne3");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Pattern.LITERAL

Par défaut, certains métacaractères ont une signification particulière lors de la recherche de la correspondance.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("(.*)");
    Matcher matcher = pattern.matcher("Java");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Correspondance trouvée

Le mode LITERAL désactive l'interprétation de tous les métacaractères.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    Pattern pattern = Pattern.compile("(.*)", Pattern.LITERAL);
    Matcher matcher = pattern.matcher("Java");
 
    if (matcher.matches()) {
      System.out.println("Correspondance trouvée");
    } else {
      System.out.println("Aucune correspondance");
    }
  }
}

Résultat :
Aucune correspondance

Pattern.MULTILINE

Par défaut, les métacaractères « ^ » et « $ » correspondent respectivement au début et à la fin de la chaîne de caractères d'entrée entière. La correspondance ne tient pas compte des terminaisons de ligne intermédiaires.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "Java\nJava";
    Pattern pattern = Pattern.compile("Java$");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
 
  }
}

Résultat :
1 : Java
Nb occurrences = 1

Le mode MULTILINE permet de modifier le comportement par défaut des métacaractères ^ et $ pour qu'ils prennent en compte chaque ligne.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "Java\nJava";
    Pattern pattern = Pattern.compile("Java$", Pattern.MULTILINE);
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
 
  }
}

Résultat :
1 : Java
2 : Java
Nb occurrences = 2

Il est aussi possible d'utiliser l'option équivalente en début de l'expression régulière.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texte = "Java\nJava";
    Pattern pattern = Pattern.compile("(?m)Java$");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
 
  }
}

Résultat :
1 : Java
2 : Java
Nb occurrences = 2

 

23.2.11. Le support d'Unicode

Les séquences d'échappement Unicode telles que \u20AC sont supportées dans les motifs.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
  
  public static void main(String[] args) {
    String texte = "200?";
    Pattern pattern = Pattern.compile("\\u20AC");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
 
  }
}

Résultat :
1 : ?
Nb occurrences = 1

Un caractère Unicode peut également être représenté dans une expression régulière en utilisant sa notation hexadécimale (valeur du code point hexadécimal) en utilisant la séquence \x{...}.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
  
  public static void main(String[] args) {
    String texte = "200?";
    Pattern pattern = Pattern.compile("\\x{20AC}");
    Matcher matcher = pattern.matcher(texte);
 
    int nbOcc = 0;
    while (matcher.find()) {
      nbOcc++;
      System.out.println(nbOcc + " : " + texte.substring(matcher.start(), matcher.end()));
    }
    System.out.println("Nb occurrences = " + nbOcc);
 
  }
}

Résultat :
1 : ?
Nb occurrences = 1

Lorsque l'indicateur UNICODE_CHARACTER_CLASS est utilisé alors les classes de caractères prédéfinies et les classes de caractères POSIX ci-dessous sont conformes à la recommandation de l'annexe C : Propriétés de compatibilité des expressions régulières Unicode.

Classes

Matches

\d

Un chiffre : \p{IsDigit}

\D

Tout sauf un chiffre : [^\d]

\s<\code>

Un caractère d'espacement : \p{IsWhite_Space}

\S<\code>

Tout sauf un caractère d'espacement : [^\s]

\w<\code>

Un caractère dans des mots : [\p{Alpha}\p{gc=Mn}\p{gc=Me}\p{gc=Mc}\p{Digit}\p{gc=Pc}
\p{IsJoin_Control}]

\W<\code>

Un caractère qui n'est pas dans un mot : [^\w]

 

23.3. Les remplacements de texte

Les expressions régulières peuvent être utilisées pour effectuer des remplacements des occurrences trouvées dans la chaîne de caractères à traiter.

 

23.3.1. Les remplacements avec la classe Matcher

La classe Matcher propose plusieurs méthodes pour effectuer des remplacements des correspondances trouvées par une autre chaîne.

 

23.3.1.1. Les méthodes replaceFirst() et replaceAll()

Plusieurs méthodes permettent d'effectuer des remplacements : les méthodes replaceFirst() et replaceAll() permettent de faire un ou plusieurs remplacements du motif s'il est trouvé.

La méthode replaceFirst() remplace uniquement la première occurrence du motif de la chaîne de caractères par celle fournie en paramètre.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texteJava = "Java est orienté objet, Java supporte les regex";
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher(texteJava);
    String textePerl = matcher.replaceFirst("Perl");
    System.out.println(textePerl);
  }
}

Résultat :
Perl est orienté objet, Java supporte les regex

La méthode replaceAll() remplace toutes les occurrences du motif de la chaîne de caractères par celle fournie en paramètre.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texteJava = "Java est orienté objet, Java supporte les regex";
    Pattern pattern = Pattern.compile("Java");
    Matcher matcher = pattern.matcher(texteJava);
    String textePerl = matcher.replaceAll("Perl");
    System.out.println(textePerl);
  }
}

Résultat :
Perl est orienté objet, Perl supporte les regex

L'expression régulière peut être plus complexe.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "Le langage <b>Java</b>";
    Pattern pattern = Pattern.compile("<b>([^<]+)</b>");
    Matcher matcher = pattern.matcher(chaine);
    System.out.println(matcher.replaceAll(""));
  }
}

Résultat :
Le langage

Il est possible de faire référence à la valeur d'un groupe comme valeur de remplacement en utilisant la syntaxe $n, où n correspond au numéro du groupe de capture, dans la chaîne de remplacement.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "Le langage <b>Java</b>";
    Pattern pattern = Pattern.compile("<b>([^<]+)</b>");
    Matcher matcher = pattern.matcher(chaine);
    System.out.println(matcher.replaceAll("$1"));
  }
}

Résultat :
Le langage Java

 

23.3.1.2. Les méthodes appendReplacement() et appendTail()

La classe Matcher fournit également les méthodes appendReplacement() et appendTail() pour le remplacement du texte.

Elles s'utilisent conjointement pour le remplacement d'occurrences dans un objet de type StringBuffer.

La méthode appendReplacement() exécute une étape d'ajout et remplacement : elle remplace la précédente correspondance par la chaîne fournie en paramètre et ajoute ensuite le résultat au StringBuilder.

La méthode appendTail() doit être invoquée après les invocations de la méthode appendReplacement() pour copier le reste de la chaîne de caractères. Si cette méthode n'est pas invoquée après des remplacements, alors la chaîne de caractères résultantes ne sera pas complète.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    Pattern pattern = Pattern.compile("Perl");
    Matcher matcher = pattern.matcher("Perl est orienté objet, Perl supporte les regex");
    StringBuffer sb = new StringBuffer();
    while (matcher.find()) {
      matcher.appendReplacement(sb, "Java");
    }
    matcher.appendTail(sb);
    System.out.println(sb.toString());
  }
}

Résultat :
Java est orienté objet, Java supporte les regex

L'utilisation combinée de ces deux méthodes peut avoir le même effet que l'utilisation de la méthode replaceAll() mais elle peut aussi permettre d'avoir un contrôle très fin sur chacun des remplacement effectué.

Exemple :
package fr.jmdoudoux.dej.regex;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    Pattern pattern = Pattern.compile("Perl");
    Matcher matcher = pattern.matcher("Perl est orienté objet, Perl supporte les regex");
    StringBuffer sb = new StringBuffer();
    int nbOcc = 1;
    while (matcher.find()) {
      matcher.appendReplacement(sb, "Java("+nbOcc+")");
      nbOcc++;
    }
    matcher.appendTail(sb);
    System.out.println(sb.toString());
  }
}

Résultat :
Java(1) est orienté objet, Java(2) supporte les regex

 

23.4. L'utilisation d'expressions régulières dans les méthodes de la classe String

Plusieurs méthodes de la classe String attendent en paramètre une expression régulières :

Plusieurs méthodes proposent des équivalents à celles de la classe Pattern ou de la classe Matcher :

Remarque : la méthode replace() n'utilise pas d'expression régulière.

Ces méthodes sont pratiques mais elles ne sont pas optimisées d'un point de vue performance.

La méthode matches() de la classe String permet de vérifier la correspondance intégrale de la chaîne avec l'expression régulière fournie. Elle renvoie un booléen qui indique si cette correspondance est vérifiée ou non.

Une invocation de cette méthode de la forme str.matches(regex) est équivalente à Pattern.matches(regex, str).

Exemple :
package fr.jmdoudoux.dej.regex;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "Java 11";
    System.out.println(chaine.matches("Java [0-9]*"));
  }
}

Résultat :
true

La méthode replaceFirst() de la classe String remplace la première occurrence trouvée correspondant à l'expression régulière fournie par la valeur fournie.

Une invocation de cette méthode de la forme str.replaceFirst(regex, rempl) est équivalente à
Pattern.compile(refex).matcher(str).replaceFirst(rempl).

Exemple :
package fr.jmdoudoux.dej.regex;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texteJava = "Perl est orienté objet, Perl supporte les regex";
    String textePerl = texteJava.replaceFirst("Perl", "Java");
    System.out.println(textePerl);
  }
}

Résultat :
Java est orienté objet, Perl supporte les regex

La méthode replaceAll() de la classe String remplace toutes les occurrences trouvées correspondant à l'expression régulière fournie par la valeur fournie.

Une invocation de cette méthode de la forme str.replaceAll(regex, rempl) est équivalente à Pattern.compile(refex).matcher(str).replaceAll(rempl).

Exemple :
package fr.jmdoudoux.dej.regex;
 
public class RegEx {
 
  public static void main(String[] args) {
    String texteJava = "Perl est orienté objet, Perl supporte les regex";
    String textePerl = texteJava.replaceAll("Perl", "Java");
    System.out.println(textePerl);
  }
}

Résultat :
Java est orienté objet, Java supporte les regex

Remarque : elle ne remplace que les caractères ASCII. Si ce comportement ne correspond pas au besoin, il faut utiliser un Matcher avec le mode adéquat.

La méthode split() de la classe String découpe la chaîne de caractères en fonction des correspondances de l'expression régulière fournie.

Elle possède deux surcharges :

Méthode

Rôle

String[] split(String regex, int limit)

Découper la chaîne de caractères selon les correspondances de l'expression régulière fournie. Une invocation de cette méthode sous la forme str.split(regex, n) est équivalente à Pattern.compile(regex).split(str, n).

Le paramètre limit contrôle le nombre de fois que le motif est appliqué et affecte donc la longueur du tableau retourné.

Les correspondances ne sont pas incluses dans le tableau retourné.

String[] split(String regex)

Découper la chaîne selon les correspondances de l'expression régulière fournie. Cette méthode fonctionne de la même manière que l'invocation la méthode split() à deux arguments avec l'expression régulière fournie et un argument limite de zéro. Les chaînes vides à la fin ne sont pas incluses dans le tableau retourné.


Exemple :
package fr.jmdoudoux.dej.regex;
 
public class RegEx {
 
  public static void main(String[] args) {
 
    String chaine = "element1:element2:element3";
    String[] elements = chaine.split(":"); 
    for (String element : elements) {
      System.out.println(element);
    }
  }
}

Résultat :
element1
element2
element3

 


22. L'API Stream Partie 3 : Les API avancées Imprimer Index Index avec sommaire Télécharger le PDF    
Développons en Java
v 2.40   Copyright (C) 1999-2023 .