Pattern
. Cette classe a pour objet de compiler l'expression régulière fournie.
La seconde classe,
Matcher
, permet de comparer une expression régulière à un texte, et de faire différentes opérations dessus.
matches
de la classe
Pattern
.
Exemple 40. Utilisation de la méthode statique
Pattern.matches
String texte = "Quand le ciel bas et lourd" ; // texte à tester boolean b = Pattern.matches("a.*", texte) ;
b
sera vrai si texte contient une chaîne de caractères commençant par la lettre
a
. Dans ce moteur d'expression régulière,
.*
correspond à n'importe quel nombre de caractères quelconques.
Une façon plus complexe, mais équivalente, de l'écrire est de passer par un objet
Matcher
.
Exemple 41. Comparaison par utilisation d'un
Matcher
String texte = "Quand le ciel bas et lourd" ; // texte à tester Pattern p = Pattern.compile("a.*") ; Matcher m = p.matcher(texte) ; boolean b = m.matches() ;
matcher
, et de faire des opérations plus complexes qu'une simple recherche.
Pattern
, il s'agit de la méthode statique
compile
, qui prend une expression régulière en paramètre, et un flag optionnel. Nous verrons les flags utilisables dans la suite.
Ensuite, on trouve trois méthodes utilitaires :
flags()
,
pattern()
et
toString()
, qui retournent respectivement les éventuels flags déclarés sur ce pattern, et le pattern proprement dit. La méthode
toString()
est surchargée dans la classe
Pattern
, elle retourne simplement l'expression régulière de ce pattern.
La méthode statique
matcher(String)
permet de comparer les textes passés en paramètre avec cette expression régulière, sans avoir à construire d'objet
Matcher
.
Enfin les deux méthodes
split(String)
et
split(String, int)
permettent de découper un texte en fonction de l'expression régulière passée en paramètre. Elles retournent toutes les deux un tableau de
String
, limité au nombre de cases éventuellement passé en paramètres. Voyons son fonctionnement sur un exemple.
Exemple 42. Découpage par la méthode
matcher.split()
Pattern p = Pattern.compile("\n") ; String texte = "Pen Duick\n" + "Belle Poule\n" + "Perle\n" + "Pourquoi pas" ; String [] bateaux = p.split(texte) ; for (int i = 0 ; i < tableau.length ; i++) { System.out.println("[" + i + "] = " + tableau[i]) ;
[0] = Pen Duick [1] = Belle Poule [2] = Perle [3] = Pourquoi pasPasser un paramètre entier à la méthode
split()
signifie que l'on veut arrêter le découpage lorsque l'on a le nombre de morceaux donnés. Passer 3 à cette méthode donnera donc le résultat suivant.
[0] = Pen Duick [1] = Belle Poule [2] = Perle\nPourquoi pasOn a bien trois éléments dans le tableau de résultat. Enfin la méthode statique
quote()
permet de retourner la chaîne de caractères passée en paramètres en tant que chaîne à rechercher.
Matcher
. Les méthodes de la classe
Pattern
ne permettent que d'utiliser les opérations basiques.
Les trois opérations de recherche supportés par la classe
Matcher
sont les suivantes :
matches()
: cette méthode permet de tester si le texte correspond à l'intégralité de l'expression régulière. Par exemple, ce texte commence-t-il par la lettre
a
?
lookingAt()
: cherche si le texte commence par le pattern fourni. La différence entre
matches()
et
lookingAt()
est un peu subtile, nous allons voir un exemple.
find()
: examine le texte, et recherche les occurrences du pattern dedans. Des appels successifs à cette méthode permettent de balayer l'ensemble du texte recherché. Lorsque cette méthode a trouvé une occurence du pattern, alors il est possible d'analyser le texte en utilisant les méthodes
start()
,
end()
et
group()
.
matches()
et
lookingAt()
.
Tableau 2. Différences entre
matches()
et
lookingAt()
Pattern | Texte |
matches()
|
lookingAt()
|
---|---|---|---|
P.*
|
Pen Duick |
true
|
true
|
P.*
|
Belle Poule |
false
|
false
|
P
|
Pen Duick |
false
|
true
|
P
|
Belle Poule |
false
|
false
|
P.*
correspond à une chaîne de caractères de longueur quelconque, commençant par un
P
. Il ne correspond pas au texte
Belle Poule
, en revanche il correspond à
Pen Duick
, que ce soit au sens de
matches()
et
lookingAt()
.
Le pattern
P
correspond à la chaîne de caractères
"P"
, ne comportant que ce seul caractère. La méthode
matches()
compare les chaînes de caractères dans leur intégralité. Elle retourne donc
false
pour les textes
Pen Duick
et
Belle Poule
.
En revanche, la méthode
lookingAt()
cherche si le texte commence par le pattern considéré. Pour
P
, la réponse est
true
pour
Pen Duick
,
false
pour
Belle Poule
.
group()
permet de retourner le dernier élément textuel trouvé, en général par un
find()
.
Prenons un premier exemple très simple, qui va nous permettre d'introduire la notion de pattern
greedy
et
reluctant
.
Analysons le texte suivant :
"(1 + 2)*4 / (2 + 2)"
, dans lequel on souhaite repérer les groupes de parenthèses :
"(1 + 2)"
et
"(2 + 2)"
. Analysons ce texte à l'aide du pattern
"\\(.*\\)"
. Ce pattern impose que la région trouvée commence par une parenthèse ouvrante (
\\(
), suivi de n'importe quelle suite de caractères, éventuellement vide (
.*
), fermée par une parenthèse fermante (
\\)
).
Les doubles backslashes sont ici présents car les parenthèses ont une signification particulière lorsque l'on écrit des expressions régulières. Elles doivent donc échapper à cette signification. Le code d'analyse est le suivant.
Exemple 43. Exemple d'utilisation de
find()
et
group()
Pattern p = Pattern.compile("\\(.*\\)") ; String s = "(1 + 2)*4 / (2 + 2)" ; Matcher m = p.matcher(s) ; while (m.find()) { System.out.println("groupe = " + m.group()) ; }
> groupe = (1 + 2)*4 / (2 + 2)Que s'est-il passé ? En fait, si l'on examine de près l'expression retournée, elle commence bien par une parenthèse ouvrante, et se termine bien par une parenthèse fermante. Damned ! Ce n'est pas le résultat que nous avions prévu ! Heureusement, comme les choses en ce bas monde ne sont pas toujours si mal faites, ce cas a en fait été prévu. En réalité, les expressions régulières fonctionnent par défaut de façon greedy . Cela signifie que lorsqu'un pattern est cherché dans un texte, c'est toujours la portion la plus large possible de ce texte qui est sélectionnée. Dans notre exemple, lorsque le moteur de recherche rencontre la première parenthèse ouvrante, il marque le début du groupe, et va marquer la fin du groupe sur la dernière parenthèse fermante qu'il trouve, sans prendre en compte l'autre parenthèse ouvrante sur laquelle il est passé. Le mode qui nous intéresse pour répondre à notre problème n'est pas le mode greedy , mais le mode reluctant , qui prend la première parenthèse fermante, plutôt que la dernière. Il est activé en ajoutant un
?
à notre pattern, juste après le
*
. Notre pattern devient alors le suivant.
Pattern p = Pattern.compile("\\(.*?\\)") ; // notons le ? supplémentaireLe résultat devient alors :
> groupe = (1 + 2) > groupe = (2 + 2)On voit bien là le côté merveilleux des expressions régulières : rechercher dans le fouillis d'une syntaxe complètement hermétique, le caractère qui fait que tout le système s'effondre...
replaceAll()
de la classe
Matcher
, comme dans l'exemple suivant.
Exemple 44. Utilisation de
matcher.replaceAll()
String texte = "un - deux - trois - quatre" ; Pattern p = Pattern.compile("-") ; Matcher m = p.matcher(texte) ; String texteRemplace = m.replaceAll(";") ;
> un ; deux ; trois ; quatreIl existe également une méthode
replaceFirst()
qui ne remplace que la première occurrence du morceau de texte recherché.
Cette méthode ne peut pas convenir si l'on souhaite remplacer chaque occurrence du morceau de texte, par un autre morceau, variable, éventuellement dépendant du morceau remplacé. Dans ce cas, il faut utiliser la méthode
appendReplacement()
, suivant la méthode de programmation suivante.
Exemple 45. Utilisation de
matcher.appendReplacement()
String texte = "un - deux - trois - quatre FIN" ; Pattern p = Pattern.compile("-") ; Matcher m = p.matcher(texte) ; StringBuffer sb = new StringBuffer() ; int i = 1 ; while (m.find()) { m.appendReplacement(sb, "[" + i++ + "]") ; } m.appendTail(sb) ;
StringBuffer
à l'issue de l'exécution de ce code est :
> un [1] deux [2] trois [3] quatre FINL'appel final à la méthode
appendTail()
est indispensable : c'est lui qui recopie la fin du texte dans le
StringBuffer
(ici
"FIN"
).
region(int, int)
, qui fixe les limites de cette région.
Les limites basses et hautes de cette région peuvent être retrouvées grâce aux deux méthodes
regionStart()
et
regionEnd()
. Elles retournent toutes deux un
int
. Par défaut
regionStart
est inclus dans la région, et
regionEnd
en est exclu.