Cours de JavaScript


précédentsommairesuivant

VII. Les chaînes de caractères

VII-A. Introduction

Nous avons, dans les chapitres qui précèdent, à maintes reprises utilisé des chaînes de caractères dans le sens dont nous en parlions au §II.7Les-Objets, c'est-à-dire, en tant qu'éléments de type simple. Nous allons voir dans ce chapitre que les chaînes ont pourtant l'apparence d'objets auxquels est associé ce qui semble être de nombreuses méthodes de traitement (modification d'apparence, extraction, recherche de caractère, de motif, etc.). En tout cas, la syntaxe servant à les invoquer suit exactement celle d'objets bénéficiant de propriétés et de méthodes… Qu'en est-il exactement ?

Directement liées aux chaînes de caractères, nous présenterons ensuite le formalisme lié aux expressions régulières, l'objet utilisant ce formalisme pour établir des modèles ainsi que les méthodes et propriétés qui sont liées.

VII-B. Finalement, les chaînes de caractères sont-elles des objets à part entière ?

En préambule, et pour se faire rapidement une opinion sur le sujet, nous allons utiliser l'opérateur typeof sur une chaîne. Vous allez donner une valeur quelconque dans une fenêtre de dialogue (qui a la propriété de toujours restituer une chaîne). Cette valeur sera affectée à une variable, V dont, en retour, vous obtiendrez le type.

Allons-y...

On voit bien que l'on n'obtient pas le type Object, comme ce serait effectivement le cas si cette chaîne était véritablement un objet de type composé, mais un type spécifique : string.

Nous allons à présent redéfinir la variable par V=new String(<la valeur que vous venez d'entrer>) et voyons ce que donne

typeof  V

En fait, à chaque type primitif, que ce soit pour les chaînes, mais aussi pour les nombres et les booléens, correspond une classe d'objets composés celle-ci constituant une enveloppe du type primitif correspondant. Pour une variable de type primitif, son enveloppe va contenir, outre la valeur affectée à la variable, toutes les propriétés et méthodes de la classe correspondante. Ainsi, si l'on définit une variable de type simple et que l'on essaie au travers d'elle d'accéder à des propriétés ou méthodes de la classe correspondant à son type, JavaScript opère une conversion implicite et l'objet ainsi créé est utilisé en ses lieu et place. Cet objet ainsi créé est temporaire. Dès qu'il n'est plus utilisé, sa place est récupérée et il n'existe donc plus.
Cette transformation s'opère aussi en sens inverse : si nous définissons un objet de type String et que nous désirons le faire intervenir dans une expression de concaténation de chaînes utilisant l'opérateur +, l'objet est transformé vers son alias de type primitif afin que l'expression soit correctement évaluée.

Ce mécanisme de transformation peut sembler très élégant, mais s'opérant au coup par coup, on peut craindre une perte d'efficacité. Mais que l'on se rassure (!…), JavaScript fait cette transformation très rapidement.

On a vu, toujours au §II.7Les-Objets, la distinction qui se faisait entre objets de type simple et objets de classe qui, pour les uns, étaient traités par valeur et, pour les autres, par référence. À présent que l'on sait que les objets de type simple ont leur alter ego en tant qu'objet de classe, on peut craindre un comportement différent selon que l'on utilise l'une ou l'autre forme… Voyons ce qu'il en est au travers d'un exemple.

Voici deux petits programmes totalement comparables hormis le fait que dans l'un, on utilise des variables de type simple et dans l'autre des objets. Il met en jeu des chaînes de caractères, car c'est de cela que nous traitons dans ce chapitre, mais vous pourrez vérifier que le fonctionnement serait identique avec des nombres (classe Number()).

 
Sélectionnez
<script language="JavaScript">
  function Conc(x,y){
    x+=y;
  } 
  var T = "GA";
  var S = T;
  Conc(S,T);
  alert("S="+S+" T="+T);
</script>
 
Sélectionnez
<script language="JavaScript">
  function Conc(x,y){
    x+=y;
  }
  var T = new String("GA");
  var S = T;
  Conc(S,T);
  alert("S="+S+" T="+T);
</script>

On constate que les comportements sont totalement identiques et, en particulier, que les modifications opérées dans la fonction Conc ne se répercutent pas vers l'extérieur. Il s'agit bien d'un passage par valeur au sens où on l'entend habituellement. Il est heureux que les comportements soient identiques, car du fait des transformations implicites dont il a été question plus haut, si tel n'avait pas été le cas, cela aurait pu rendre les résultats aléatoires.

Voyons à présent pour un programme équivalent ce qui ce serait passé si l'on avait eu affaire à un véritable objet de classe.

 
Sélectionnez
<script language="JavaScript">
  function MaChaine(val){
    this.valeur=val;
  }
  function ConcObj(x,y){
    x.valeur+=y.valeur;
  }
  
  var T = new MaChaine("GA");
  var S = T;
  ConcObj(S,T);
  alert("S="+S.valeur+" T="+T.valeur);
</script>

On constate que non seulement la valeur de S.valeur a bien changé, mais de plus, on s'aperçoit que celle de T.valeur a changé aussi. Est-ce un bogue ? Pas du tout ! C'est au contraire tout à fait normal si l'on se souvient que toute manipulation d'objets de classe se fait par référence. Dans cet exemple, que s'est-il passé ?

On a tout d'abord créé un objet de la classe MaChaine dont la propriété valeur a été affectée à "GA", puis cet objet ainsi initialisé a été affecté à la variable T.T est donc une référence vers l'objet créé. On a ensuite créé une nouvelle variable S à laquelle on a affecté la référence T. Ainsi, dès lors S et T pointent vers le seul et même objet qui vient d'être créé. Dans ConcObj, on modifie la propriété valeur de celui-ci via la référence S. Au passage on constate bien que cette modification sera pérenne et ne sera pas détruite au sortir de la fonction. En effet l'affichage de S.valeur nous le prouve. Lorsque nous allons lire pour affichage T.valeur, S et T pointant vers le même objet, il est donc normal que la valeur retournée soit elle aussi affectée par la modification intervenue dans ConcObj.

Image non disponible

VII-C. Les propriétés et méthodes associées aux chaînes

Nous avons vu, dans le paragraphe précédent, que les chaînes avaient, selon leur contexte d'utilisation, soit un statut de type simple, soit celui d'objet de la classe String. À ce propos, signalons au passage que ce nom désigne, comme nous l'avons vu dans l'un des exemples précédents le constructeur de la classe. Parmi les méthodes qui seront présentées, nous distinguerons deux types : les méthodes d'environnement qui permettent d'obtenir une copie de la chaîne cible dans un environnement HTML et les méthodes de traitement proprement dites qui permettent diverses manipulations sur la chaîne cible elle-même.

VII-C-1. La propriété length

Nous avons déjà rencontré ce terme lorsque nous avons étudié les tableaux. Dans ce contexte, la propriété length indiquait la taille dynamique du tableau concerné (le nombre de ses éléments de premier niveau). Nous avons vu aussi, dans ce même chapitre, qu'il existait une méthode qui, appliquée à un tableau, donnait pour résultat la chaîne composée de ses éléments séparés par un séparateur (par défaut « , » ou précisé). On voit donc que tableaux et chaînes ont des points de convergence très forts et on ne s'étonnera donc pas de rencontrer de nombreuses utilisations de cette ambivalence.
L'utilisation de cette propriété se présente sous la forme <chaîne>.length. Ici, nous vous demandons de fournir une chaîne quelconque et en retour, une fenêtre vous indiquera le nombre de caractères de celle-ci :

VII-C-2. Les méthodes d'environnement

Ces méthodes permettent de simplifier l'écriture d'un programme JavaScript devant générer du code HTML. Sans rentrer dans des détails inutiles qui feraient référence au langage HTML, on peut se limiter à citer :

  • anchor(<nom>) ;
  • big() ;
  • blink() ;
  • bold() ;
  • fixed() ;
  • fontcolor(<couleur>) ;
  • fontsize(<taille>) ;
  • italics() ;
  • link(<reference>) ;
  • small() ;
  • strike() ;
  • sub() ;
  • sup().

VII-C-3. La méthode charAt()

Avant toute chose, et comme nous l'avons vu pour les tableaux, il convient de préciser que le premier caractère d'une chaîne se situe à l'indice 0. De façon générale, une chaîne de n caractères (length) comporte des éléments depuis l'indice 0 jusqu'à l'indice n-1. La méthode dont il est ici question permet d'obtenir le caractère de la chaîne cible dont l'indice est fourni en paramètre. Par exemple, l'exécution de, car="JavaScript".charAt(4) aura pour effet d'affecter à la variable, car le caractère « S ». Si la valeur entière fournie en paramètre est en dehors de l'intervalle [0, length-1], la valeur rendue est une chaîne vide (de longueur 0).

VII-C-4. La méthode charCodeAt()

Cette méthode est semblable dans son fonctionnement à la précédente. Elle aussi à pour rôle de rechercher dans la chaîne cible le caractère dont l'indice est fourni en paramètre, mais ici, ce n'est pas le caractère lui-même qui est rendu par la méthode, mais son code Unicode, c'est-à-dire, une valeur entière comprise entre 0 et 65 535 (codage non signé sur 16 bits).

Nous allons montrer ici trois exemples : les deux premiers montreront des utilisations de cette méthode. Le troisième montrera l'utilisation d'une méthode statique, propriété du constructeur String(), lui-même : fromCharCode(). Celle-ci permet de construire une chaîne de caractères à partir d'une liste de codes Unicode passée en paramètre.

charCodeAt() & fromCharCode()… Résultat
 
Sélectionnez
"JavaScript".charCodeAt(3)
 
Sélectionnez
"JavaScript".charCodeAt(10)
 
Sélectionnez
String.fromCharCode(97,98,99,100)

Le premier test délivre bien le code Unicode de 'a', 97 ; le second aboutit à une erreur, car l'indice 10 est au-delà des indices de caractères de la chaîne concernée ; quant au troisième, il reconstruit une chaîne à partir de la succession des codes donnés en paramètres et on retrouve bien 'a' de code 97 et les trois caractères suivants dans l'ordre alphabétique.

Notez la façon dont cette méthode est utilisée et en particulier le préfixe correspondant au nom du constructeur.

VII-C-5. La méthode concat()

Cette méthode, comme son nom l'indique, permet de concaténer (accoler) à la chaîne cible référencée la série de valeurs fournies en paramètre.

Comportement de concat()… Résultat
 
Sélectionnez
"Java".concat("Sc",'ri',"pt")
 
Sélectionnez
"Java".concat("Script",1.3)

On constate que la concaténation s'opère bien avec des chaînes (jusque-là, rien de bien étonnant), mais aussi avec des valeurs numériques. En effet, toute valeur numérique passée en paramètre est systématiquement transformée en chaîne de caractères, ce qui permet ainsi la concaténation.

Notons au passage que l'on peut obtenir les mêmes résultats d'une façon beaucoup plus simple : en employant l'opérateur de concaténation de chaîne +.

VII-C-6. Les méthodes indexOf() et lastIndexOf()

À l'opposé de charAt() qui permettait de déterminer le caractère apparaissant à une position particulière dans une chaîne, on pourrait imaginer une méthode permettant de déterminer l'indice de l'occurrence d'un caractère dans la chaîne référencée. En fait, cette méthode est beaucoup plus puissante que cela. Elle permet, en effet, d'indiquer l'indice où débute une sous-chaîne fournie en paramètre dans la chaîne référencée (bien sûr, si la sous-chaîne est réduite à un seul caractère, on obtient le résultat recherché précédemment). Mais la sous-chaîne recherchée peut apparaître plusieurs fois dans la chaîne référencée. Dans ces conditions, comment balayer toutes les occurrences sans devoir tronquer, au fur et à mesure, le début de la chaîne de référence (en supposant que la recherche s'opère de gauche à droite). La méthode comporte un second paramètre qui précise à partir de quel indice la recherche doit être entreprise. Ainsi, sans modifier la chaîne source, on pourra énumérer toutes les occurrences d'une sous-chaîne précisée et en indiquer l'indice de début.

Finalement, la forme générale d'utilisation de cette méthode est la suivante :

 
Sélectionnez
<chaine source>.indexOf(<sous chaine>[,<debut>])

On constate que le second argument précisant le début de la recherche est optionnel. En l'absence de celui-ci, la recherche s'opère à partir de l'indice 0. Si aucune occurrence n'est trouvée, soit parce qu'elle n'existe pas, soit parce que la valeur de début est plus grande que la longueur de la chaîne, cette méthode renvoie la valeur -1.

Dans les versions anciennes de Netscape (< 4), dans le cas où le début était hors des limites normales, la méthode retournait une chaîne vide au lieu de la valeur -1.
Voici un petit texte et le programme qui va nous servir à compter de nombre de fois où la lettre « e » apparaît…

Le texte
Sélectionnez
"Il est certain que JavaScript est un langage simple à apprendre, mais il n'en est pas pour autant rudimentaire... "
Le programme
Sélectionnez
var Compt = 0;
var Deb = Texte.indexOf('e');
while (Deb != -1) {
  Compt++;
  Deb = Texte.indexOf('e',++Deb);
}
alert("Nbre d'occurrences de 'e' : " + Compt);

Nous venons de voir la méthode indexOf() qui opère la recherche de gauche à droite. Il existe une autre méthode, lastIndexOf() qui elle opère une recherche de droite à gauche. L'utilisation de celle-ci a la même forme que la précédente ; comme elle, elle reçoit en second paramètre optionnel une valeur entière indiquant l'indice du début de recherche.

VII-C-7. La méthode slice()

Cette méthode n'est pas sans rappeler celle, de même nom, qui a été présentée lors de l'étude des tableauxLa-méthode-slice(). Elle permet, à partir de la chaîne source référencée, d'extraire la sous-chaîne débutant à l'indice désigné par le premier paramètre et s'achevant à l'indice désigné par le second paramètre. Le second paramètre est optionnel. En l'absence de celui-ci, c'est toute la fin de la chaîne (à partir de l'indice indiquant le début) qui est rendue.

Par exemple, "JavaScript".slice(4,6) rend la valeur 'Sc', "JavaScript".slice(4) rend la valeur 'Script'. Ces deux exemples montrent qu'alors que le caractère désigné par l'indice de début fait partie de la sous-chaîne rendue, celui désigné par l'indice de fin n'en fait pas partie. Les valeurs données en paramètres peuvent être négatives. Dans ce cas, elles indiquent des indexations partant de la fin de la chaîne. On doit interpréter cette valeur, x, négative, par length + x. Autrement dit, -1 indique le dernier caractère (indice length-1), -2, l'avant-dernier (indice length-2), etc.

Il est à noter que le fait d'attribuer une valeur négative au paramètre de début ne fonctionne pas sous Internet Explorer. Ce dernier ramène cette valeur à 0 et c'est donc tout le début de la chaîne qui est rendu.

Voyons quelques utilisations directes de cette méthode :

Comportement de slice()… Résultat
 
Sélectionnez
"JavaScript".slice(4)
 
Sélectionnez
"JavaScript".slice(4,6)
 
Sélectionnez
"JavaScript".slice(6,4)
 
Sélectionnez
"JavaScript".slice(3,-3)
 
Sélectionnez
"JavaScript".slice(-5,-1)
 
Sélectionnez
"JavaScript".slice(-1,-5)

Les exemples 1, 2 et 4 fonctionnent bien comme annoncé précédemment. Sous Netscape, les exemples 3 et 6 provoquent une erreur, car l'indice de début est supérieur à celui de fin ; par contre, sous Internet Explorer, 3 retourne une chaîne vide, tandis que 6 s'évalue en fonction de la remarque faite plus haut, l'indice de début (-1) étant remplacé par 0. Cette particularité d'Internet Explorer se manifeste de la même façon pour l'exemple 5 qui se comporte donc lui aussi de manière différente selon le navigateur utilisé.

VII-C-8. La méthode split()

Ces deux méthodes permettent, comme slice(), d'extraire une sous-chaîne de la chaîne source. substring() constitue l'ancêtre de slice() en ce sens qu'elle apparaissait déjà dans JavaScript 1.0 alors que slice() a été introduite dans JavaScript 1.2. Elle fonctionne exactement de la même manière (le caractère d'indice <début> fait partie de la sous-chaîne extraite alors que le caractère d'indice <fin> en est exclu), mais elle ne supporte pas les paramètres négatifs comme nous l'avons vu pour slice().

La méthode substr() qui est apparue dans JavaScript 1.2 accepte en paramètre une, voire deux valeurs entières, la première indiquant, comme pour les précédentes, l'indice de début de la sous-chaîne à extraire, la seconde, facultative, indiquant la longueur de celle-ci. Toujours comme pour les précédentes, en l'absence de ce second paramètre, c'est toute la fin de la chaîne qui est extraite.

VII-C-9. Les méthodes substring() et substr()

Nous avons étudié dans le chapitre précédent la méthode join()La-méthode-join() de la classe Array(). Rappelons que celle-ci permet de transformer un tableau en une chaîne de caractères dans laquelle les éléments du tableau source sont séparés, par défaut, par le caractère « , » ou, si elle est précisée, par une chaîne quelconque. La méthode split() est en quelque sorte la « méthode inverse » puisque à partir de la chaîne source référencée et en la découpant sur la base d'un séparateur fourni en paramètre, elle construit un tableau dont les éléments sont les sous-chaînes ainsi séparées.
Précisons que le séparateur peut être une chaîne de caractères éventuellement vide. Dans ce cas particulier, la chaîne est découpée caractère par caractère. Il peut aussi prendre la forme d'une expression régulière, mais nous reviendrons là-dessus plus tard.

Pour montrer un exemple d'utilisation de cette méthode, reprenons le texte utilisé plus haut et calculons le nombre de mots.

 
Sélectionnez
"Il est certain que JavaScript est un langage simple à apprendre, mais il n'en est pas pour autant rudimentaire&#8230;"
 
Sélectionnez
var Compt = Texte.split(' ').length;
alert("Nbre de mots: " + Compt);

Efficace, n'est-ce pas ?… Oui, mais approximatif ! En effet, on n'est pas à l'abri de caractères d'espacement (' ') doublés, ce qui aurait pour conséquence de « créer » des mots fantômes. Par ailleurs, les élisions ne sont pas prises en compte ici : « n'en » compte pour un seul mot ce qui pour les puristes peut paraître une aberration. Nous verrons plus loin que l'utilisation d'une expression régulière nous permettra de pallier ces travers.

VII-C-10. Les méthodes toLowerCase() et toUpperCase()

Enfin, pour en terminer, voici deux méthodes permettant de « normaliser » des chaînes de caractères, lorsque cela est nécessaire, en rendant une copie de la chaîne source dont tous les caractères alphabétiques sont convertis en minuscules pour l'une, en majuscules pour l'autre.

À titre d'exemple d'utilisation, on peut imaginer que parmi les données saisies dans un formulaire, certaines (initiale de prénom, nom, ville, etc.) puissent être systématiquement converties en minuscules. On peut aussi reprendre l'exemple précédent dans lequel on recherchait le nombre d'occurrences d'une certaine lettre par la méthode indexOf(). Dans cet exemple, on fournissait en paramètre une lettre minuscule, mais pour prévoir l'éventualité d'occurrence de cette même lettre sous la forme d'une majuscule, on aurait dû opérer cette recherche sur une copie du texte où toutes les lettres auraient été transformées en minuscules.

Il y a d'autres méthodes de la classe String, mais nous avons choisi de ne les présenter que dans le prochain chapitre, car, mettant en jeu les expressions régulières, il serait hasardeux d'en parler avant d'avoir pris connaissance de celui-ci.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Jacques Guizol. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.