Chapitre 8 : Les expressions régulières

 

1 - Structure des expressions régulières

 

En JavaScript, les expressions régulières, dont nous allons voir la grande utilité au travers des exemples qui émaillerons ce paragraphe, constituent une classe d'objets, la classe RegExp. Nous verrons qu'elles agissent sur des chaînes de caractères pour permettre de les analyser, les filtrer et de chercher des motifs contenus dans celles-ci avec un niveau de précision adapté à la problématique (du plus grossier au plus fin). Notons que ce concept d'expressions régulières, très fortement inspiré (c'est un euphémisme) de celui du langage Perl, n'est apparu dans JavaScript que depuis sa version 1.2.


Comment définit-on des expressions régulières ?

Un objet "expression régulière" peut être introduit et éventuellement défini grâce au constructeur de la classe, RegExp(), en écrivant simplement une instruction du type var R = new RegExp(). Dans cet exemple, l'expression régulière R existe, mais aucune valeur ne lui a été affectée. Comme pour les constructeurs que nous avons déjà rencontrés, on aurait pu définir sa valeur en la précisant, sous forme d'une chaîne de caractère, en paramètre du constructeur. Un tel objet peut aussi être introduit de façon littérale en utilisant des marqueurs spécifiques délimitant l'expression : '/'. Nous pourrions ainsi avoir : var R = /a/. (Cette expression, dans le cadre de l'analyse d'une chaîne impose simplement que celle-ci contienne le caractère a). Une écriture équivalente utilisant le constructeur eut été : var R : RegExp("a").

Dans une expression régulière, outre les caractères devant se retrouver physiquement dans la ou les chaînes qui seront traitées par celle-ci, on rencontre un nombre non négligeable de caractères "outils' dont il est important de bien saisir la sémantique pour pouvoir les utiliser de façon pertinente et réaliser ainsi des modèles efficaces et concis. Ces caractères sont de trois types : les caractères définissant des littéraux, les caractères d'ensemble, les caractères de groupement et enfin les caractères de répétition.


Les littéraux

Ces caractères contiennent, outre tous les caractères alphanumériques, une représentation des caractères non imprimables (tabulation, retour chariot, saut de ligne, saute de page, etc.) ainsi que des caractères qui étant utilisés en tant que caractères "outils" doivent néanmoins avoir une représentation différente pour pouvoir être analysés dans une chaîne. Ces caractères littéraux seront en introduit par le caractère "backslash" : \.

En voici la liste :

Caractère
Signification
Alphanumérique
Lui-même
\ /
\ \
\ .
\ +
\ *
\ ?
\ |
\ }
\ {
\ (
\ )
\ [
\ ]
/
\
.
+
*
?
|
}
{
(
)
[
]
\t
\r
\n
\f
\v
tabulation horiz.
retour chariot
saut de ligne
saut de page
tabulation vert.
\xxx
\xhh
car. ASCII de code octal xxx
car. ASCII de code hexa hh

A titre d'exercice, vous allez donner un texte en entrée en tâchant d'y inclure la lettre dont le code ASCII octal 112 est attendu... essai

Si vous voulez un peu tricher, consultez une table des codes ASCII... Mais elle est souvent en décimal et hexadécimal ! Ce sera une occasion pour jongler entre les bases de numération !!! (On n'a rien sans rien ;-))



Les caractères d'ensemble

Les caractères d'ensemble permettent de construire à la demande une collection de caractères ou de désigner des collections prédéfinies. Ils pourront indiquer que l'un quelconque des caractères qu'ils représentent doit apparaître ou, au contraire, qu'aucun des caractères qu'ils représentent ne doit apparaître.
Il faut ajouter que dans le cas où les codes des caractères de la collection à désigner constituent une suite, on pourra représenter cette suite par seulement le premier et le dernier caractères séparés par un tiret. Citons, par exemple les caractères de l'alphabet majuscules ([A-Z]), minuscules ([a-z]), les chiffres décimaux ([0-9]), hexadécimaux ([0-9A-F]), etc.

Si, au contraire on désire représenter un caractère pouvant être quelconque hormis appartenir à un ensemble parfaitement cerné, on utilisera le caractère de complémentarité ^ en début d'ensemble ; par exemple [^!?.,;] désigne tout caractère qui n'est pas un signe de ponctuation. Voici la liste de ces caractères :

Caractère
Signification
[....]
[^....]
.

\s

\S

\w

\W

\d

\D
Un qcq des caractères contenus
Aucun des caractères contenus
Caractère qcq sauf saut de ligne
équivaut à [^ \n]
Tout caractère de césure
équivaut à [ \t\r\n\f\v]
Aucun caractère de césure
équivaut à [^ \t\r\n\f\v]
Tout caractère alphanumérique
équivaut à [a-zA-z0-9]
Aucun caractère alphanumérique
équivaut à [^a-zA-z0-9]
Tout chiffre (décimal)
équivaut à [0-9]
Aucun chiffre (décimal)
équivaut à [^0-9]

Si, par exemple, on désire analyser le mot JavaScript contenant ou pas les majuscules et encadré de deux caractère de césure, nous définirons ainsi le modèle : /\s[Jj]ava[Ss]cript\s/. Attention : avec cette expression on accepte l'écriture Javascript qui n'est pas gênante, mais aussi javaScript qui peut ne pas être souhaitée.

ATTENTION : A l'intérieur d'un ensemble (ou d'un complémentaire d'ensemble), pour représenter des "caractères outils" du premier sous-groupe ("/", "+", "*", "?", "(", ")", etc), point n'est besoin de les faire précéder du caractère "\". Les seuls qui doivent l'être (car ils introduisent une ambiguïté) sont : "\" et "]"


Les caractères de groupement (et de référencement)

Le caractère de groupement a pour fonction de regrouper plusieurs éléments constituant un sous-motif du modèle en un seul élément. Le groupement s'opère simplement en l'encadrant d'une paire de parenthèses. Il y a plusieurs utilités à pouvoir regrouper des éléments :

  • on peut ainsi faire porter les caractères de répétition que nous verrons plus loin sur l'ensemble des éléments du modèle ainsi regroupés. Ainsi, c'est cet ensemble qui sera dupliqué et non les éléments constitutifs individuellement ;
  • on peut aussi référencer ce groupement de façon à prévoir une autre occurrence des éléments de la chaîne analysés avec lesquels il a été apparié, plus loin dans le modèle.

Le référencement dont il vient d'être question s'opère par l'apparition dans l'expression régulière du caractère \n où n désigne le n° de parenthésage auquel il fait référence. Pour illustrer d'un exemple simple, supposons que l'on veuille analyser la chaîne JavaScript encadrée de simples ou doubles guillemets (les deux devant être identiques bien entendu).

Si l'on analyse à l'aide de l'expression régulière /['"]JavaScript['"]/, les écritures 'JavaScript" et "JavaScript' seront reconnues correctes, ce que nous refusons. En fait, l'expression d'analyse devra être de la forme : /(['"])JavaScript\1/. Essayez !
Grâce à cet essai, on constate que cette expression autorise en début de chaîne le caractère ' ou le caractère ", mais une fois le premier analysé, il contraint le second à lui être identique.


Les caractères de répétition

Ce sont des caractères dont la fonction est de prévoir dans le modèle un nombre d'occurrences successives du littéral, du groupement ou d'élément d'ensemble sur lequel ils portent.

Caractère
Signification
*
+
?
{n}
{n,m}
{n,}
Un nombre indéfini de fois [0,x], x >= 0
Au moins une fois [1,x], x > 0
Eventuellement une fois [0,1]
Exactement n fois
Au moins n et au plus m fois [n,m]
Au moins n fois [n,x], x >= n

Chacun sait que tout individu français est immatriculée par un numéro INSEE plus connu pour certains d'entre eux (pas pour tous, hélas) sous le nom de numéro "sécu". Ce numéro comporte un formatage bien précis : un chiffre pour le sexe (1 ou 2), deux pour l'année de naissance, deux pour le mois, deux pour le département de naissance, trois pour la ville, trois pour l'ordre, tous ces champs étant séparés par un espace, puis un slash (/) et enfin deux chiffres pour la clé. Vous allez proposer une expression régulière, la plus courte possible, permettant d'opérer l'analyse de ce type d'information, puis vous la testerez contre votre numéro INSEE afin de vérifier soit l'un, soit l'autre...

ATTENTION : Veillez à bien refermer parenthèses, crochets, accolades, etc. si vous en utilisez, sinon, sous Netscape, la compilation de votre expression provoquera une erreur. Si d'aventure cela se produit, remontez simplement à la fenêtre précédente de l'historique du navigateur.



Le caractère de choix

Le caractère de choix permet de prévoir, dans l'expression régulière, le choix entre différents sous-motifs, séparés par le caractère |. Comme pour les caractères de répétitions, celui-ci peut porter sur un ou plusieurs littéraux, ensembles ou groupements.

Par exemple, pour analyser une adresse, on peut imaginer l'expression suivante :

Dans cette expression apparaissent plusieurs groupements. Parmi eux, certains constituent des choix (statut du destinataire, de la voie), d'autres permettent de faire porter un caractère de répétition sur plusieurs sous-motifs successifs. Par exemple, 'identité du destinataire est composée d'au moins un mot, son nom (d'où le caractère de répétition + qui impose au moins 1) ; ce nom peut être composé ; il peut être précédé ou suivi d'un prénom, lui-même pouvant être composé chacun de ces éléments répondant au même modèle. Avec une telle expression, on peut analyser une adresse du type :

M. Jean-Claude Vandamme
17, Rue de la Castagne
31500 TOULOUSE

Les caractères de positionnement

Nous avons seulement vu, jusqu'ici, des appariements de caractères. Il existe aussi la possibilité d'apparier et donc, de contraindre encore, selon la position dans la chaîne. Cela est d'un grand intérêt car on va pouvoir mettre en place des ancrages de motifs. Prenons l'exemple d'un identificateur qui doit commencer forcément par une lettre éventuellement suivie de lettres ou de chiffres.
Vous seriez tenté de proposer, avec beaucoup d'assurance, l'expression régulière suivante : /[A-Za-z]\w*/. Considérons donc la chaîne 03Trois1. Je suis au regret de vous dire que l'appariement va réussir et donc que 03Trois1 sera bien pris comme un identificateur !!!

En fait pour toutes les expressions régulières que l'on a vues jusqu'ici, la vérification se contentait de tester si dans la chaîne fournie, une partie de celle-ci répondait au modèle décrit par l'expression régulière. Si vous avez réussi à faire l'exercice sur le n° INSEE, proposez à présent quelque chose du style "Tartempion 1 85 06 13 055 312/51.. Ouf!!!". Vous allez constater que tout baigne !!! Précisément, la partie centrale qui apparaît ici en italique aura répondu au modèle. De la même manière, dans 03Trois1, c'est en fait Trois1 qui aura constitué un parfait identificateur !!

Il faudrait donc contraindre l'analyse à appliquer le modèle dès le début de la chaîne afin que [A-Za-z] ne pouvant s'apparier à 0 , 03Trois1 ne soit pas retenu comme identificateur. Voici ces caractères de positionnement :

Caractère
Signification
^
$
\b
\B
Ancre en début de chaîne
Ancre en fin de chaîne
Ancre sur limite de mot (entre \w et \W)
Ancre sur non limite de mot

Ainsi, en utilisant l'expression /^[A-Za-z]\w*/, l'analyse d'un identificateur sera pertinente. Pour le numéro INSEE, et seulement lui, on utilisera le caractère ^ en début d'expression tandis que le caractère $ la terminera.

Ces caractères de positionnement, dont on comprend bien l'énorme importance, sont hélas, trop peu nombreux. Ce défaut disparaîtra avec JavaScript 1.3 qui permettra de définir le type de positionnement que l'on veut. Par exemple (?=[0-9]) permet de définir un ancrage avant un chiffre, tandis que (?![0-9]) désigne un ancrage avant tout caractère qui ne soit pas un chiffre (Å(?=[^0-9])).

Ne pas confondre le caractère d'ancrage en début avec le caractère de complémentarité. Même s'ils ont une représentation similaire, il n'y a aucune ambiguïté car l'un se situe obligatoirement en début d'expression (après /) tandis que l'autre ne peut apparaître qu'en début de définition d'ensemble (après [).

Les attributs d'appariement

Les attributs d'appariement sont au nombre de deux. Ils se placent après l'expression régulière sur laquelle ils portent.

On dispose de l'attribut i qui indique s'il est présent que l'analyse de la chaîne doit se faire sans tenir compte de la casse des caractères. Cela permet, entre autres, de simplifier les expressions et si nous reprenons les exemples précédents, l'expression décrivant un identificateur pourra s'écrire /^[A—Z]\w*/, celle des nombres hexadécimaux, /^[A—F0—9]+$/i.

Nous allons présenter dans les prochains paragraphes des méthodes permettant, sur la base d'une expression régulière, de rechercher des sous-chaînes de la chaîne de caractères source. En présence de l'attribut g, ce sont effectivement toutes les portions s'appariant avec l'expression qui seront recherchées. Dans le cas contraire, , la recherche s'arrêtera dès la première rencontrée.

2 - Les propriétés & méthodes de la classe RegExp

La propriété lastIndex (non implanté sous I.E.)

C'est une propriété accessible en lecture/écriture dans laquelle est enregistré, dans le cas de recherches globales, l'indice dans la chaîne source, à partir duquel la recherche devra reprendre. Cette propriété est en particulier utilisée par les méthodes test() et exec() que nous allons voir.


La propriété source

Accessible en lecture seulement, cette propriété est une chaîne de caractères contenant le texte de l'expression régulière référencée. Par exemple, si vous avez fourni une expression régulière, dans l'exercice sur le numéro INSEE, vous pouvez la revoir en cliquant ici.


Les propriétés global et ignoreCase (non implanté sous I.E.)

Ces deux propriétés sont elles aussi accessibles seulement en lecture sont deux booléens permettant de récupérer la valeur des attributs de l'expression régulière référencée. Si global est vrai, cela signifie que la recherche d'appariements est globale à toute la chaîne ; si ignoreCase est vrai il n'est pas tenu compte de la casse des caractères.


  Nous allons voir à présent les propriété statiques de la classe RegExp. Rappelons qu' à la différence des propriétés d'instance qui s'applique à tout objet instance de la classe, chaque propriété statique, est unique dans la classe et donc commune à tous les objets de cette classe.

Les propriétés RegExp.lefContext & RegExp.rightContext (non implanté sous I.E.)

A chaque fois qu'une recherche d'appariement entre une expression régulière et une chaîne de caractères est opérée, par des méthodes que nous verrons plus loin, qu'elles soient de la classe RegExp ou de la classe String, ces indicateurs seront positionnés. Le premier contiendra la partie de la chaîne située à gauche du dernier appariement effectué, tandis que le second contiendra la partie droite.

A noter que ces propriétés sont équivalentes respectivement à RegExp["$`"] et RegExp["$'"].


Les propriétés RegExp.lastMatch & RegExp.lastParen (non implanté sous I.E.)

Les deux précédentes propriétés permettent de récupérer les contextes gauche et droit du dernier appariement opéré. Ce qui se trouve entre les deux, c'est à dire la partie de la chaîne source mise en concordance avec l'expression régulière sera disponible dans la propriété lastMatch.

A l'intérieur de la sous-chaîne appariée contenue dans lastMatch, on peut de plus dégager la partie qui s'est appariée au dernier groupement du précédent appariement. Celle-ci est contenue dans lastParen. Il est intéressant de noter que l'on peut obtenir les sous-chaînes mises en appariement avec les neuf premiers groupements par l'intermédiaire des propriétés RegExp.$1, RegExp.$2, ..., RegExp.$9.

A noter aussi que ces propriétés sont équivalentes respectivement à RegExp["$&"] et RegExp["$+"].


 

Un bon exemple valant plus que de longs discours, étudiez le comportement de ces différentes propriétés sur la base d'une expression régulière de la forme /([A-Z]([a-z]+)\d([,.]))/g et d'une chaîne de caractères ayant pour valeur : "Un1,Deux2,Trois3,Quatre4,Cinq5.".



La propriété RegExp.multiline (non implanté sous I.E.)

Ce booléen permet de préciser si la chaîne sur laquelle agit l'expression régulière contient une seule ou plusieurs ligne. Selon le cas, le comportement pourra être différent et c'est en particulier le cas pour les ancrages de début et de fin. Dans le cas où le texte n'est pas précisé multiligne, ^ représente le début de la chaîne et $, la fin de la chaîne, même s'il contient des littéraux \n. Dans le cas où ce même texte est déclaré multiligne, ^ représente le début e et $, la fin de chaque ligne. Modifions légèrement les chaînes et expressions régulières du précédent exemple.

La chaîne utilisée va contenir plusieurs lignes : "Un1,\nDeux2,Trois3,\nQuatre4,Cinq5.".

Par ailleurs, l'expression régulière va pouvoir revêtir deux formes :

Forme 1 = /^([A-Z]([a-z]+)\d([,.]))+$/g  et  Forme 2 = /^([A-Z]([a-z]+)\d([,.]\n?))+$/g

Enfin, nous allons considérer le cas où multiline est vrai et celui où multiline est faux... Voyons ce que cela donne...

Forme 1
Forme 2
multiline VRAI
multiline FAUX

A noter que cette propriété est équivalente à RegExp["$*"].


La méthode compile(<chaîne>[, <attributs>])

Lorsque dans un script interviennent plusieurs expressions régulières, soit qu'elles soient fournies en paramètre soit même par l'utilisateur comme ce fut le cas dans l'exercice sur le numéro INSEE, plutôt que de créer pour chacune un objet RegExp, il est sûrement avantageux d'utiliser, si cela s'y prête, un seul objet. Dans ces conditions, les expressions régulières successives qui lui seront affectées seront tout d'abord fournies sous forme de chaîne qu'il faudra donc transformer ensuite en un objet de type RegExp. Cette transformation d'une chaîne de caractères vers une expression régulière est opérée par la méthode compile(). On peut déplorer que cette méthode ne récupère pas les erreurs de syntaxe qui peuvent apparaître dans le modèle fourni sous forme de chaîne. En particulier, une erreur de parenthésage dans les groupements rend Netscape fou de rage !...


 

Les deux méthodes de la classe RegExp qu'il nous reste à voir ont une utilité voisine et complémentaire. Les deux opèrent un appariement entre l'expression régulière référencée et la chaîne de caractères passée en paramètre, mais l'une renvoie seulement un booléen signifiant qu'au moins un appariement a pu avoir lieu, alors que l'autre renvoie, sous forme d'un tableau, des informations plus complètes sur les sous-expressions appariées.

Ces deux méthodes affectent et utilisent deux propriétés qui n'ont pas été citées plus haut : index et input. index contient la position du caractère de la chaîne à partir duquel l'appariement a eu lieu et input fait référence à la chaîne elle-même. Si l'une ou l'autre de ces méthode est rappelée sans paramètre, par défaut, c'est la chaîne référencée par input qui sera utilisée.


La méthode test(<chaîne>) (buggé sous I.E.)

Cette méthode essaie d'opérer un appariement entre l'expression régulière référencée et la chaîne de caractères donnée en paramètre à partir de l'indice spécifié par la propriété lastIndex. Si un appariement est possible, lastIndex est réactualisé et la valeur booléenne renvoyée est true. Dans le cas contraire, la valeur renvoyée est false et lastIndex est remis à 0.


La méthode exec(<chaîne>) (buggé sous I.E.)

L'autre méthode d'appariement, s'évalue sur le même type de donnée que la précédente (expression régulière référencée et chaîne de caractères en paramètre). Comme elle, elle essaie donc de procéder à l'appariement de ces deux données. En cas d'échec, la valeur de retour est null. Dans le cas contraire, la valeur retournée est un tableau contenant en 0, la sous-chaîne appariée avec l'expression régulière et dans le éléments suivants les sous-chaînes appariées avec les éventuels groupements parenthésés. Par ailleurs, en cas d'appariement, cette méthode actualise lastIndex, si bien que si elle est rappelée avec la même référence, elle procède à une nouvelle recherche à partir de cet emplacement. Comme pour la méthode précédente, en cas d'échec, lastIndex est remis à 0.

3 - Les méthodes de la classe String mettant en jeu les expressions régulières

La méthode match(<expr. régul.>)

Cette méthode qui est à rapprocher de la méthode exec() que l'on vient de voir, hormis le fait que celle-ci fait référence à un chaîne et a pour paramètre une expression régulière. Elle permet de rechercher dans la chaîne référencée la ou les portions de celle-ci qui répondent à un modèle (expression régulière) fourni en paramètre. Dans le cas où aucune portion de la chaîne ne répond au modèle, la valeur retournée est null. Dans le cas contraire, la valeur rendue est un tableau construit de façon différente selon que l'on ne recherche qu'une (la première à gauche) mise en concordance ou chacune d'elles. Dans le premier cas, le premier élément du tableau comporte la sous chaîne répondant au modèle et les éléments suivants contiennent les sous-parties correspondant aux éventuelles sous expressions parenthésées du modèle. Dans le cas où toutes les concordances sont recherchées, le tableau rendu se limite à chacune des parties de la chaîne source répondant au modèle.

On voit donc que la similitude que l'on trouvait au début entre exec() et match() n'est pas totale puisque alors que exec() n'évalue qu'un seul appariement, match() effectue tout les appariements que l'expression autorise sur la chaîne passée en paramètre. En particulier, l'attribut g prendra pleinement sa signification dans match() ce qui n'est pas le cas dans exec().

Dans l'exemple qui suit, nous allons comparer les comportements de match() et exec() dans un contexte de recherche globale ou pas.

Le programme...
  var Tab;
var Texte="Un1,\nDeux2,Trois3,\nQuatre4,Cinq5.";
 
 var Model=/([A-Z]([a-z]+)\d([,.]))+/g;  var Model=/([A-Z]([a-z]+)\d([,.]))+/;
 Tab = Model.exec(Texte);  Tab = Texte.match(Model);
if (Tab==null) alert("Aucun appariement n'est possible !");
else {
  S="";
  for(var i=0;i<Tab.length;i++)
    S+= "En "+i+" : "+Tab[i]+"\n";
  alert(S);
}
 

Voici le canevas du programme qui va être utilisé, dans lequel, est mise en évidence (enfin, j'espère !...) la combinatoire des tests qui vont être fait...

match()
exec()
avec attribut g
sans attribut g

NB : Cette méthode met à jour les propriétés statiques de RegExp.


La méthode replace(<expr. régul.>,<remplacement>)

Cette méthode référence une chaîne de caractères dans laquelle une sous-chaîne appariée avec l'expression régulière donnée dans le premier paramètre va être remplacée par le second paramètre. Généralement,, le remplacement s'opère par une chaîne de caractères. Mais il peut advenir que ce soit la sous-chaîne extraite de l'appariement que l'on veuille modifier tout en gardant tout ou partie des éléments qui la composent. Dans ces conditions il est souhaitable de pouvoir accéder à ces parties sous forme paramétrée afin de les répercuter dans le paramètre de remplacement sous la forme et dans l'ordre désirés. Cela est possible si l'on prévoit dans l'expression régulière des groupement parenthésés que l'on peut ensuite référencer dans le paramètre remplacement par les caractères $1, $2,...,$9. Plusieurs caractères faisant référence à diverses portions de la chaîne sont ainsi disponibles :

Caractère
Signification
$1, $2,..., $9
$&
$'
$`
$+
sous-chaînes appariées aux 9 premiers groupements
sous-chaîne appariée à l'expression régulière
sous-chaîne à droite de l'appariement      (idem rightContext)
sous-chaîne à gauche de l'appariement   (idem leftContext)
dernier groupement apparié

Exemple : Les dates sont codées différemment dans les pays anglo-saxons et en France. Sur la base de deux chiffres pour le jour (jj), le mois(mm) et l'année (aa), une date sera codée en France jjmmaa, alors qu'en Angleterre, par exemple, elle sera codée mmjjaa. Quant au séparateur, qu'il soit " ", "/" ou "-", il demeurera identique. En supposant que le texte sur lequel porte l'opération soit référencé par la variable Txt, le problème sera résolu par :

Txt.replace(/\s*(\d{1,2})([ \/-])(\d{1,2})\2(\d{1,2})\s*/,"$3$2$1$2$4")

ATTENTION : Ces caractères n'ont de validité qu'à l'intérieur de l'appel à la méthode replace(). Si vous essayez de les utiliser à l'extérieur vous aurez de grosses surprises !... A moins que vous preniez en compte qu'ils sont aussi des propriétés statiques de la classe RegExp et que vous les utilisiez sous la forme qui convient.
Par ailleurs, de façon logique, lorsque aucun appariement n'est possible, la chaîne référencée reste inchangée.


La méthode search(<expr. régul.>)

Nous avons vu, dans le chapitre précédent, la méthode indexOf() qui permettait de déterminer l'emplacement d'une sous-chaîne dans une chaîne donnée en référence. En particulier, nous avons mis en évidence ses faiblesses en l'utilisant dans un exemple où l'on cherchait à compter le nombre d'occurrences d'un caractère car pour prendre en compte la casse de celui-ci, il nous aurait fallu procéder à deux appels, l'un pour rechercher les occurrences minuscules et l'autre pour les majuscules. La méthode présentée ici bénéficie des possibilités offertes par les expressions régulières pour rechercher une sous-chaîne vérifiant un modèle particulier. Reprenons l'exemple auquel il était fait référence à l'instant :

Le texte...
"Il est certain que JavaScript est un langage simple à apprendre. Il n'en est pas pour autant rudimentaire... "
Le programme (version search)...
var Compt = 0;
var Deb = Texte.search(/i/i);
while (Deb != -1) {
  Compt++;
  Deb = RegExp.rightContext.search(/i/i);
}
alert("Nbre d'occurrences de 'i' : " + Compt);
Les résultats...

 indexOf()

 search()

On constate qu'avec indexOf("i"), la valeur retournée correspond au nombre de caractères très précisément identiques à celui donné en paramètre. Tandis qu'avec search() on peut préciser, grâce à l'attribut i que la casse est indifférente. On voit aussi que malgré le fait que search() ne dispose pas de paramètre indiquant l'indice de début de recherche, comme c'était le cas pour indexOf(), grâce à la propriété statique rightContext, on peut balayer la totalité de la chaîne fournie. Ces propriétés de classe n'existant pas sous Internet Explorer, cet exemple ne fonctionnera pas de façon satisfaisante sous ce navigateur.

NB : Cette méthode met à jour les propriétés statiques de RegExp.


Pour terminer signalons une autre méthode de la classe String pouvant utiliser une expression régulière. Nous avons vu dans le chapitre précédent la méthode split(), qui, sur la base d'un séparateur fourni en paramètre, transformait la chaîne référencée en un tableau dont les éléments étaient la succession des sous-chaînes ainsi séparées.

Cette méthode, en l'état, atteint vite ses limites. Si, par exemple, nous voulons obtenir un tableau constitué des différents mots d'un texte sur la base d'un seul séparateur, on va se heurter à plusieurs problèmes. Tous les caractères de ponctuations vont soit faire partie de mots (c'est le cas de la virgule ou du point, toujours accolés au mot qui les précède), soit constituer des mots eux-mêmes (c'est le cas de ; ? ! qui réclament un espace de part et d'autre). Cela sans compter les espaces doublés ou oubliés après la virgule ou le point, les apostrophes, etc. Bref, les mots dégagés et donc le nombre de mots trouvés seraient entachés d'erreur.

En utilisant l'expression régulière /[ ',.;?!-]+/ en tant que séparateur, voire, beaucoup plus simplement /\W+/, le problème sera résolu !....

Considérons,par exemple, le texte suivant :

Ho cà ! n'ai-je pas lieu de me plaindre de vous ?
Et, pour n'en point mentir, n'êtes vous pas méchante
De vous plaire à me dire une chose affligeante ?

(Tartuffe de Molère)

Pour les deux expressions régulières, le résultat est le même. Par contre, selon que vous serez sous Netscape ou Explorer, le résultat sera 33 pour l'un, ce qui est faux, ou 32 pour l'autre, ce qui est exact. Effectivement, regardez bien l'énumération des mots répertoriés. Celle-ci se termine par une virgule sous Netscape, preuve qu'il y a un mot vide comptabilisé, ce qui n'est pas le cas avec Explorer.

NB : Vous remarquerez aussi, sous Explorer, que dans la deuxième méthode, les caractères accentués ne sont pas reconnus comme appartenant à \w. Ils sont donc inclus dans \W et donc interprétés comme séparateurs. Si bien qu'un mot contenant n caractères accentués sera décompté pour n+1 mots. En particulier dans cet exemple, vous pouvez constater que "méchante" compte pour deux mots à cause du "é", alors que "à" (de "...plaire à me dire..."), disparaît. Par ailleurs dans le texte analysé, "çà" a en fait été orthographié "cà", ce qui explique que le "c" subsiste et qu'un mot soit ainsi détecté. Si l'orthographe avait été correcte, il manquerait un mot au décompte final... Conclusion ?


Testez vos acquis concernant les expressions régulières !!!!



Chapitre 7 : Les chaînes de caractères SommaireChapitre 9 : Les liens