Infoforall

Javascript 05 : GESTION DES STRINGS

Attention : activité en reconstruction pendant quelques jours.

L’aspect visuel est devenu l’un des aspect fondamental de la communication moderne : donner un pavé de 400 pages sans image à lire à quelqu’un, il a plus de chances de laisser tomber que de s’y mettre de façon enthousiaste. Néanmoins, si la forme est importante pour rendre un document attrayant, le fond est important également. Voilà pourquoi nous allons travailler aujourd’hui sur la gestion des caractères à travers l’objet qui le gère en informatique : le string ou chaîne de caractères.

L’utilisation de ce structure de données va nous être utile pour aborder un thème courant en informatique : la cryptologie ou l’art de coder/décoder un message pour le rendre illisible et incompréhensible en cas d’interception.

Nous utiliserons pour votre première vraie page interactive le classique rot13, avant de compliquer les choses dans les leçons suivantes.

Le rot13 Le rot13

1 - Les boucles FOR sur les objets itératifs

Nous avons vu dans l'activité précédente que le string est un ensemble ordonné de caractères et qu'on peut les lire un à un en utilisant une boucle FOR.

Si vous n'étes pas familier avec le code ci-dessous, il vaut mieux repartir voir la partie sur le FOR dans les deux activités précédentes :

var texte = "Bonjour !";

for (var caractere of texte) {

    console.log(caractere);

}

console.log("Hors de la boucle");

01° Sans lancer le code, répondre à la question suivante : Que devrait-on voir dans la console du navigateur ?

Voir l'exécution :

texte :

caractere :

>>>

02° Rajouter ce code dans un fichier javascript rattaché à une page HTML de votre choix. Vérifiez ainsi si votre réponse précédente est bonne.

...CORRECTION ...

B

o

n

j

o

u

r

 

!

03° Quel est le numéro d'index de la première lettre du string, le B ? Comment afficher ce caractère à l'aide de la variable texte et cet index ?

...CORRECTION ...

B possède le numéro d'index 0 puisqu'on commence à numéroter à partir de 0.

Pour atteindre ce B, il faut utiliser les crochets : texte[0]

04° Combien de caractères possède cette chaîne de caractères ?

...CORRECTION ...

Elle possède 9 caractéres : B - o - n - j - o - u - r - ESPACE - !.

L'espace est donc un 'caractère' pour l'ordinateur.

Si les réponses vous semblent étranges, il serait mieux de repartir voir la partie FOR de l'activité précédente.

Si vous cherchez des renseignements complémentaires, w3schools.

2 - Encodage ASCII des caractères

Nous allons profiter de ce cours pour faire un peu de cryptologie, mais nous devons d’abord comprendre comment l’ordinateur stocke les caractères qu’on lui envoie. Le principe est simple : l’ordinateur n’est qu’une machine à calculer, il ne sait stocker que des nombres.

Lorsqu’il travaille sur une image, il gère des nombres.

De même, lorsqu’il travaille sur des lettres (des « caractères »), il gère des nombres. Ainsi, il faut décider par quel nombre coder A, B, a, b, 0, 8 …

Le codage basique des caractères est le codage ASCII (qui fut publié en 1963) : on associe à chaque caractère un nombre compris entre 0 et 127. On obtient donc 128 possibilités.

05° A votre avis, les caractères ASCII sont donc codés sur combien de bits puisque le code commence à 0 et finit à 127 ? On rappelle que si X est le nombre de bits, on obtient un nombre de possibilités de 2 à la puissance X, 2X.

Rappel : un octet correspond à 8 bits : M =  1010 1111  vaut M = 128 + 32 + 8 + 4 + 2 + 1 = 175. Vous pouvez aller revoir la leçon sur la gestion des images par Python si vous avez besoin d'un rappel plus conséquent. Avec 8 bits, on obtient un codage allant de 0 à 255, soit 256 possibilités.

...CORRECTION ...

Pour stocker 128 valeurs (allant de 0 à 127, n'oubliez pas qu'on commence à zéro), il faut 7 bits.

En effet, 27 vaut 128.

A l'époque, la mémoire était une denrée rare et on ne stockait même pas les caractères sur un octet (8 bits) !

ASCII se prononce “ASKI” pour American Standard Code for Information Interchange : il s’agit d’une norme d’encodage informatique des caractères alphanumériques standards de l’alphabet et de quelques « caractères » spéciaux permettant à l’ordinateur de comprendre qu’on veut faire une tabulation, un saut à la ligne …

Revoilà l’exemple typique du "Hello World" qui donnerait :

Contenu de la mémoireA l’écranValeur codanteen binaire
Start of Text2000 0010
HH721001000
ee1011100101
ll1081101100
ll1081101100
oo1111101111
Space32100000
WW871010111
oo1111101111
rr1141110010
ll1081101100
dd1001100100
End of Text3000 0011

On remarquera qu’on code également des “caractères” qui ne sont pas imprimés à l’écran : il s’agit de caractères de contrôle du flux en grande majorité : on peut savoir quand un texte commence, quand il est fini, quand on doit partir à la ligne, faire une tabulation … En gros, certains caractères sont faits pour être affichés, d’autres pour savoir comment on affiche.

Si vous voulez regarder la correspondance du tableau avec le code, taper votre caractère ci-dessous :

Le caractère est codé par

La prochaine fois que vous lirez  000 0010   100 1000   110 0101   110 1100  ... vous devriez maintenant un peu mieux comprendre votre ordinateur...

Et voilà la fameuse table de correspondance ASCII :

Les premiers de la liste sont des caractères de contrôle et de gestion :

TypeDécimalSymboleSignificationBinaire
Contrôle0NULL or NULNull000 0000
Contrôle1SOM or SOHStart of Heading000 0001
Contrôle2EOA or STXStart of Text000 0010
Contrôle3EOM or ETXEnd of Text000 0011
Contrôle4EOTEnd of Transmission000 0100
Contrôle5WRU or ENQEnquiry000 0101
Contrôle6RU or ACKAcknowledgement000 0110
Contrôle7BELL or BELBell000 0111
Contrôle8FE0 or BSBackspace000 1000
Contrôle9HT/SK or HTHorizontal Tab000 1001
Contrôle10LFLine Feed000 1010
Contrôle11VTAB or VTVertical Tab000 1011
Contrôle12FFForm Feed000 1100
Contrôle13CRCarriage Return000 1101
Contrôle14SOShift Out000 1110
Contrôle15SIShift In000 1111
Contrôle16DC0 or DLEData Link Escape001 0000
Contrôle17DC1Device Control 1001 0001
Contrôle18DC2Device Control 2001 0010
Contrôle19DC3Device Control 3001 0011
Contrôle20DC4Device Control 4001 0100
Contrôle21ERR or NAKNegative Acknowledgement001 0101
Contrôle22SYNC or SYNSynchronous Idle001 0110
Contrôle23LEM or ETBEnd of Transmission Block001 0111
Contrôle24S0 or CANCancel001 1000
Contrôle25S1 or EMEnd of Medium001 1001
Contrôle26S2 or SSSubstitute001 1010
Contrôle27S3 or ESCEscape001 1011
Contrôle28S4 or FSFile Separator001 1100
Contrôle29S5 or GSGroup Separator001 1101
Contrôle30S6 or RSRecord Separator001 1110
Contrôle31S7 or USUnit Separator001 1111

Et à partir de 32, on trouve les caractères dits imprimables car visibles à l'écran :

TypeDécimalSymboleSignificationBinaire
Printable32(space)(space)010 0000

L'espace est un caractère un peu spécial mais les autres sont visibles :

TypeDécimalSymboleBinaire
Printable33!010 0001
Printable34"010 0010
Printable35#010 0011
Printable36$010 0100
Printable37%010 0101
Printable38&010 0110
Printable39'010 0111
Printable40(010 1000
Printable41)010 1001
Printable42*010 1010
Printable43+010 1011
Printable44,010 1100
Printable45-010 1101
Printable46.010 1110
Printable47/010 1111
Printable480011 0000
Printable491011 0001
Printable502011 0010
Printable513011 0011
Printable524011 0100
Printable535011 0101
Printable546011 0110
Printable557011 0111
Printable568011 1000
Printable579011 1001
Printable58:011 1010
Printable59;011 1011
Printable60<011 1100
Printable61=011 1101
Printable62>011 1110
Printable63?011 1111
Printable64@100 0000
Printable65A100 0001
Printable66B100 0010
Printable67C100 0011
Printable68D100 0100
Printable69E100 0101
Printable70F100 0110
Printable71G100 0111
Printable72H100 1000
Printable73I100 1001
Printable74J100 1010
Printable75K100 1011
Printable76L100 1100
Printable77M100 1101
Printable78N100 1110
Printable79O100 1111
Printable80P101 0000
Printable81Q101 0001
Printable82R101 0010
Printable83S101 0011
Printable84T101 0100
Printable85U101 0101
Printable86V101 0110
Printable87W101 0111
Printable88X101 1000
Printable89Y101 1001
Printable90Z101 1010
Printable91[101 1011
Printable92\101 1100
Printable93]101 1101
Printable94^101 1110
Printable95_101 1111
Printable96`110 0000
Printable97a110 0001
Printable98b110 0010
Printable99c110 0011
Printable100d110 0100
Printable101e110 0101
Printable102f110 0110
Printable103g110 0111
Printable104h110 1000
Printable105i110 1001
Printable106j110 1010
Printable107k110 1011
Printable108l110 1100
Printable109m110 1101
Printable110n110 1110
Printable111o110 1111
Printable112p111 0000
Printable113q111 0001
Printable114r111 0010
Printable115s111 0011
Printable116t111 0100
Printable117u111 0101
Printable118v111 0110
Printable119w111 0111
Printable120x111 1000
Printable121y111 1001
Printable122z111 1010
Printable123{111 1011
Printable124|111 1100
Printable125}111 1101
Printable126~111 1110

Le numéro 127 correspond à la touche DEL d'effacement :

TypeDécimalSymboleSignificationBinaire
Contrôle127DEL111 1111

Pour connaître le code d’un caractère d'index position dans le string monString, on peut utiliser une méthode qui s’applique sur des strings :

monString.charCodeAt(position);

La méthode renvoie un nombre base 10 (décimal) qui correspond à la valeur encodée par le caractère à la position donnée dans le string monString.

Appliquons ceci dans une page :

Code HTML

<!DOCTYPE html>

<html>

    <head>

        <meta charset="UTF-8" />

        <title>Code ASCII avec js</title>

        <meta http-equiv="content-Language" content="fr" />

        <link rel="stylesheet" href="style_1.css" />

    </head>

    <body>

        <header>

            <h1>Codage ASCII et plus</h1>

        </header>

        <section>


            <p>Votre texte :</p>

            <p><input type="text" id="mon_string" onchange="codeChaine()" placeholder="Votre string"></p>

            <p>Codes des caractères du texte : <span id="resultat_encodage"></span></p>


        </section>

        <script src="js_chap_05.js"></script>

    </body>

</html>

Pour le JS, je vous fournis celui qui fonctionne pour un caractère unique :

function codeChaine() {

    var leTexte = document.getElementById("mon_string").value;

    var ensembleDesCodes = "";

    var caractere = leTexte[0];

    var codeDuCaractere = caractere.charCodeAt(0);

    ensembleDesCodes = ensembleDesCodes + codeDuCaractere;

    document.getElementById("resultat_encodage").innerHTML = ensembleDesCodes;

}

06° Tester le code ci-dessus sur plusieurs caractères ASCII.

07° Transformer le code pour qu'il donne un à un le codage de tous les caractères d’un string. Vous devrez réutiliser un FOR pour lire un à un les caractéres de votre string. Vérifiez que je ne me suis pas trompé avec "Hello World" : 72 101 108 108 111 32 87 111 114 108 100 .

Il va falloir utiliser vos connaissances sur les boucles for (x of y) ou for (x in y) et sur la concaténation pour créer au fur et à mesure la chaine sResultat à renvoyer à la fin.

Voilà de quoi tester les réponses de votre code :

Votre texte :

Codes des caractères du texte :

Si vraiment, il le faut, voici une correction possible (il est possible de faire de même avec un for numérique ou un for (x in y) :

...CORRECTION ...

function codeChaine() {

    var leTexte = document.getElementById("mon_string").value;

    var ensembleDesCodes = "";

    for (var caractere of leTexte) {

        var codeDuCaractere = caractere.charCodeAt(0);

        ensembleDesCodes = ensembleDesCodes + codeDuCaractere;

    }

    document.getElementById("resultat_encodage").innerHTML = ensembleDesCodes;

}

08 Avez-vous été curieux ? Tenter de lancer une chaîne de caractères qui contient des caractères bizarres pour nos amis anglo-saxons ? Des é, â et autres ç. Regardez bien la valeur affichée. Mais, mais …ça ne dépasserait pas 127 cette histoire ?

Pour l’instant, nous allons nous limiter au cas 0 à 127 avec de l’ASCII pur. Et nous compliquerons les choses ensuite. Il s'agit des encodages dits ASCII étendu (sur 1 octet) ou des encodages modernes comme l'UTF-8 qui permet d'utiliser une sorte de code universel qu'on nomme UNICODE.

Précision sur le résultat de charCodeAt(x) :

Unicode

Le code renvoyé par charCodeAt n'est pas vraiment le "code ASCII" du caractère, même si la valeur est la bonne, soyez rassuré.

En effet, nous avons vu que charCodeAt parvient à donner le code de caractères qui ne sont pas dans la table ASCII, ceux supérieurs à 127.

Sachez que charCodeAt(x) renvoie en réalité la valeur UNICODE du caractère x. Il se trouve que les codes ASCII et les codes UNICODE sont les mêmes sur la plage 0-127. C'est volontaire : UNICODE a été établi avec comme contrainte d'être compatible avec la vielle norme ASCII.

Rappel : Dans l'une des activités précédentes, nous avions vu la fonction inverse de charAtCode : String.fromCharCode(code) permet de connaître le caractère associé au nombre entier code.

String.fromCharCode(65);

Cette première instruction renvoie :  A 

'A'.charCodeAt(0);

Cette seconde instruction renvoie :  65 

3 - Le string est non mutable on vous dit !

Créer une chaîne de caractères, c’est facile en javascript :

09° Lancer le code ci-dessous qui vous montre les deux façons de créer une chaîne contenant « Hello World ! ». Attention au nom d'enregistrement de votre fichier js : js_chap_05.js

<!DOCTYPE html>

<html>

    <head>

        <meta charset="UTF-8" />

        <title>Les Strings</title>

        <meta http-equiv="content-Language" content="fr" />

        <link rel="stylesheet" href="style_1.css" />

    </head>

    <body>

        <header>

            <h1>Modification de String</h1>

        </header>

        <section>

            <p><input type="button" onclick="modificationString1()" value="Activez le script 1!"></p>

            <p><input type="button" onclick="modificationString2()" value="Activez le script 2!"></p>

        </section>

        <script src="js_chap_05.js"></script>

    </body>

</html>

function modificationString1() {

    var monTexte = "Hello World !";

    alert("monTexte contient :\n" + monTexte + "\n et son type est " + typeof(monTexte));

    alert("Si on tente de faire un typeof sur une variable inexistante, on obtient : " + typeof(haha));

}


function modificationString2() {

    var monTexte = "Hello";

    alert("monTexte contient :\n" + monTexte + "\n et son type est " + typeof(monTexte));

    var monTexte2 = monTexte + " World !";

    alert("monTexte2 contient :\n" + monTexte2 + "\n et son type est " + typeof(monTexte2));


    var monTexte3 = "1+3=";

    var monTexte4 = 4;

    alert("monTexte3 contient :\n" + monTexte3 + "\n et son type est " + typeof(monTexte3));

    alert("monTexte4 contient initialement :\n" + monTexte4 + "\n et son type est " + typeof(monTexte4));


    monTexte4 = monTexte3 + monTexte4;

    alert("monTexte4 contient ensuite :\n" + monTexte4 + "\n et son type est " + typeof(monTexte4));

}

...CORRECTION ...

monTexte contient Hello World ! et son type est string.

monTexte2 contient Hello World ! et son type est string.

monTexte3 contient 1+2= et son type est string.

monTexte4 contient initialement 3 et son type est number.

monTexte4 contient ensuite 1+2=3 et son type est string.

Si on tente de faire un typeof sur une variable inexistante, on obtient undefined.

On retrouve bien le type dynamique du langage : monTexte4 est initialement un number mais va être transformé en string lors de la concaténation.

Remarque : pour connaitre le type d'une variable, on peut utiliser typeof(mon-truc).Si on utilise une variable qui n'existe pas, on obtient ce type de réponse.

Les éléments d’une chaîne de caractères javascript ont une spécificité : on ne peut pas les modifier une fois créées. Mais, enfin … Et une méthode telle que replace ?

monTexte.replace(/o/g,'*')

Elle va bien remplacer tous les o par des étoiles (*) non ?

Non, cette fonction renvoie simplement une copie de la première chaîne en ayant changé les caractères demandés. D’ailleurs, on doit l'utiliser ainsi pour parvenir à changer la variable monTexte initiale :

monTexte = monTexte.replace(/o/g,'*')

On remplace intégralement l’ancienne variable monTexte par une nouvelle monTexte. Nous n’avons pas juste changé un caractère par ci, un caractère par là.

Oui, bon ... On ne peut pas changer le contenu sauf avec une nouvelle affectation (un =) ? C'est grave ?

Ca dépend. Si vous voulez que les modifications soient suivies oui.

Regardons le programme suivant : on crée dans un Array maListe notre liste de courses.

function modificationString2() {

    var monTexte = "Lait";

    var monTexte2 = "Carottes";

    var maListe = [ monTexte, monTexte2 ];

    alert(maListe);


    monTexte2 = "Des Chips !";

    alert(maListe);

}

10° Sans lancer le code, demandez-vous ce que vont afficher les deux alertes. Lancez ensuite le code.

On constate ceci :

...CORRECTION ...

maListe contient bien initialement les deux strings Lait et Carottes.

On change ensuite le contenu du second string.

Pourtant, maListe contient toujours ensuite les deux strings Lait et Carottes....

Comment expliquer ceci ...

Et bien, c'est assez simple si on sait ce qu'est une variable.

Pour l'instant, vous avez beaucoup travaillé avec le modèle de réprésentation le plus simple : la variable est un nom qui permet d'accéder au contenu d'une boite.

En réalité, une variable contient l'adresse d'une zone-mémoire : lorsqu'on tape monTexte, l'interpréteur ne va pas directement lire le string : à l'aide du nom de la variable, il va lire dans un tableau l'adresse pointée par ce nom de variable. Par exemple :

Nom de la variable Exemple de zone-mémoire pointée Type de données
monTexte 80 144 string
monTexte2 27 024 string

En gros, on crée une liste à partir de deux variables monTexte et monTexte2. On affiche alors la liste et on obtient ceci :

['Lait', 'Carottes']

La liste avant

On affecte un nouveau contenu à la variable monTexte2 : monTexte2 = "Des Chips !". En réalité, on va juste faire pointer cette variable vers une autre zone-mémoire :

Nom de la variable Exemple de zone-mémoire pointée Type de données
monTexte 80 144 string
monTexte2 39034 string

On affiche le tableau maListe et on obtient :

['Lait', 'Carottes']

La liste après

Comme vous pouvez le constater, le tableau maListe n'est pas modifié : ses éléments pointent toujours vers l'ancien identifiant-mémoire.

11° Lancer le code ci-dessous pour vous convaincre (encore plus) que la modification directe d'un string ne fonctionne pas. C'est normal, souvenez-vous le string est non modifiable après création. On dira également que le string est non mutable.

function modificationString() {

    var monTexte = "Hello World";

    monTexte[1] = "a";

    alert(monTexte[1]);

    alert(monTexte);

}

Et oui, ça ne fonctionne pas. Le deuxième caractère (d'index 1 donc) est toujours le 'e'. Pas moyen de modifier le string.

Conclusion : on dit que le String est non mutable. On ne peut pas modifier son contenu une fois créé. Pour avoir l'impression de modifier des données non mutables, il faut remplacer sa variable par une variable qui porte le même nom mais qui n'aura pas la même référence interne.

4 - "Modifier" un string - Le jeu du pendu

Bilan de la partie précédente : la seule façon de "modifier" un string (qui est non mutable), c'est de recréer un string qui porte le même nom.

Un exemple concret :

Voilà trois façons de "modifier" une chaîne de caractères de façon à remplacer chaque caractère par trois fois le même caractère. Ainsi :

maChaine = "Hello World!"

doit devenir

maChaine = *HHHeeellllllooo   WWWooorrrlllddd"

La première fonctionne avec un for (initialisation;condition;incrémentation) qui donne l'index des différents caractères en utilisant la propriété length.

function modificationString() {


    var monTexte = "Hello World";

    var temporaire = "";


    for (var numIndex = 0; numIndex < monTexte.length;numIndex++) {

        var rajout = monTexte.charAt(numIndex);

        temporaire = temporaire + rajout+rajout+rajout;

    }


    monTexte = temporaire;

    alert(monTexte);

}

La seconde fonctionne avec un for (in) qui donne l'index des différents caractères dans la chaine.

function modificationString() {


    var monTexte = "Hello World";

    var temporaire = "";


    for (var numIndex in monTexte) {

        var rajout = monTexte.charAt(numIndex);

        temporaire = temporaire + rajout+rajout+rajout;

    }


    monTexte = temporaire;

    alert(monTexte);

}

La troisième version (la plus simple à utiliser) est élaborée avec la boucle for (of) qui énumère les caractères un par un.

function modificationString() {


    var monTexte = "Hello World";

    var temporaire = "";


    for (var car of monTexte) {

        temporaire = temporaire + car+car+car;

    }


    monTexte = temporaire;

    alert(temporaire);

}

Voilà, vous savez maintenant comment modifier n’importe quel caractère d’une chaîne de caractères: il faut recréer une nouvelle chaîne portant le même nom !

Nous allons maintenant reprendre notre exercice du pendu que nous avions commencé dans l'une des activités précédentes. Pour cela :

Nous allons devoir comparer la lettre demandée aux différents index de motSecret. Nous allons donc utiliser un FOR numérique :

12° Recréer le HTML et le JS nommé js_chap_05.js de notre projet. Le code js proposé n'est pas opérationnel : le string temporaire est créé ici strictement identique à motMoinsSecret. On aura donc toujours juste les étoiles.

<!DOCTYPE html>

<html>


    <head>

        <meta charset="UTF-8" />

        <title>Lecture avec js</title>

        <meta http-equiv="content-Language" content="fr" />

        <link rel="stylesheet" href="style_1.css" />

    </head>


    <body>


        <header>

            <h1>Mini-jeu du pendu</h1>

        </header>


        <section>

            <p>Le mot secret est :

                <span id="mot_moins_secret" ></span>

            </p>

            <p>La lettre à vérifier :

                <input type="text" id="ma_lettre" placeholder="Insérez ici la lette voulue">

            </p>

            <p><input type="button" onclick="verifier_presence()" value="Vérifier la présence de la lettre"></p>

        </section>


        <script src="js_chap_05.js"></script>

    </body>


</html>

function verifier_presence() {


    var motSecret = "Bonjour tout le monde";

    var motMoinsSecret = document.getElementById("mot_moins_secret").innerHTML;


    // Création initiale de motMoinsSecret si vide initialement

    if ( motMoinsSecret == "" ) {

        for (var element of motSecret) {

            if (element == '-' || element == ' ') {

                motMoinsSecret = motMoinsSecret + element;

            } else {

                motMoinsSecret = motMoinsSecret + '*';

            }

        }

    }


    // Récupération de la lettre voulue

    var maLettre = document.getElementById("ma_lettre").value;


    // Création lettre par lettre de temporaire

    // Copie de motMoinsSecret à début : code à modifier

    var temporaire = "";

    for (var index in motMoinsSecret) {

        var rajout = motMoinsSecret.charAt(index);

        temporaire = temporaire + rajout;

    }


    // Modification de motMoinsSecret

    motMoinsSecret = temporaire;


    // Modification du texte dans le span mot_moins_secret

    document.getElementById("mot_moins_secret").innerHTML = motMoinsSecret;

}

Maintenant que la réponse trouvée est stockée dans le string motMoinsSecret, nous allons pouvoir le changer au fur et à mesure de la découverte des lettres. Nous voudrions faire quelque chose commme :

Bonjour tout le monde

******* **** ** *****

>>> La lettre à vérifier : o

*o**o** *o** ** *o***

>>> La lettre à vérifier : e

*o**o** *o** *e *o**e

Nous allons néanmoins devoir changer un peu la structure de notre programme : il faudra créer motMoinsSecret si la balise d'id "mot_moins_secret" est vide, sinon il faudra initialiser motMoinsSecret à partir de ce qui se trouve dans cette balise :

Nous allons devoir cette fois utiliser une boucle FOR numérique car nous avons besoin de comparer la lettre voulue et un numéro précis d'index dans les strings motSecret et motMoinsSecret.

13° Modifier le code de façon à obtenir la mise à jour correcte de la chaine motMoinsSecret lorsqu'on teste une lettre.

...CORRECTION ...

function verifier_presence() {


    var motSecret = "Bonjour tout le monde";

    var motMoinsSecret = document.getElementById("mot_moins_secret").innerHTML;


    // Création initiale de motMoinsSecret si vide initialement

    if ( motMoinsSecret == "" ) {

        for (var element of motSecret) {

            if (element == '-' || element == ' ') {

                motMoinsSecret = motMoinsSecret + element;

            } else {

                motMoinsSecret = motMoinsSecret + '*';

            }

        }

    }


    // Récupération de la lettre voulue

    var maLettre = document.getElementById("ma_lettre").value;


    // Création lettre par lettre de temporaire

    // Copie de motMoinsSecret à début : code à modifier

    var temporaire = "";

    for (var index in motMoinsSecret) {

        var dansMoinsSecret = motMoinsSecret.charAt(index);

        var dansSecret = motSecret.charAt(index);

        if (maLettre == dansSecret) {

            temporaire = temporaire + dansSecret;

        } else {

            temporaire = temporaire + dansMoinsSecret;

        }

    }


    // Modification de motMoinsSecret

    motMoinsSecret = temporaire;


    // Modification du texte dans le span mot_moins_secret

    document.getElementById("mot_moins_secret").innerHTML = motMoinsSecret;

}

Amélioration :

On remarquera qu'on crée la nouvelle string même si au final on ne modifera rien. Par exemple, si w n'est pas présent, on devra néanmoins créer la nouvelle chaine... On peut faire mieux en testant la présence ou non de w dans motSecret avec la méthode indexOf : on effectuera alors les modifications que SI la lettre à tester est bien présente.

function verifier_presence() {


    var motSecret = "Bonjour tout le monde";

    var motMoinsSecret = document.getElementById("mot_moins_secret").innerHTML;


    // Création initiale de motMoinsSecret si vide initialement

    if ( motMoinsSecret == "" ) {

        for (var element of motSecret) {

            if (element == '-' || element == ' ') {

                motMoinsSecret = motMoinsSecret + element;

            } else {

                motMoinsSecret = motMoinsSecret + '*';

            }

        }

    }


    // Récupération de la lettre voulue

    var maLettre = document.getElementById("ma_lettre").value;


    if (motSecret.indexOf(maLettre) >= 0) {

        // Création lettre par lettre de temporaire

        // Copie de motMoinsSecret à début : code à modifier

        var temporaire = "";

        for (var index in motMoinsSecret) {

            var dansMoinsSecret = motMoinsSecret.charAt(index);

            var dansSecret = motSecret.charAt(index);

            if (maLettre == dansSecret) {

                temporaire = temporaire + dansSecret;

            } else {

                temporaire = temporaire + dansMoinsSecret;

            }

        }


        // Modification de motMoinsSecret

        motMoinsSecret = temporaire;


        // Modification du texte dans le span mot_moins_secret

        document.getElementById("mot_moins_secret").innerHTML = motMoinsSecret;

    }

}

14° Testez avec le mot à découvrir Bonjour et en demandant de vérifier la lettre b minuscule. Ca fonctionne ?

Nous allons donc devoir formater les lettres : le A et le a n'ont pas le même code. Si l'utilisateur demande à savoir s'il y a des A, alors que motSecret contient des a, le programme va répondre que non, il n'y a pas de A...

Pour faire cela, nous pourrions analyser les caractères un par un et les transformer en utilisant un test IF pour chaque lettre... Heureusemet, il y a mieux : il y a les méthodes des strings.

Les méthodes des strings déja rencontrées

La méthode split : pour renvoyer un tableau (Array) composé de la décomposition d'un string. Par défaut, le caractère séparateur est l'espace.

Exemple à insérer dans un html :

    <script>


var phrase = "A AB A.B.C.";

var mots = phrase.split(" ");

console.log(mots);


    </script>

Array(3) [ "A", "AB", "A.B.C." ]

    <script>


var phrase = "A AB A.B.C.";

var mots = phrase.split(".");

console.log(mots);


    </script>

Array(3) [ "A AB A", "B", "C" ]

replace : pour obtenir un nouveau string où un caractère particulier est transformé en autre chose. Seul un caractère est modifié, le premier dans la chaîne. Si vous voulez tous les transformer, il faut utiliser une expression régulière. Attention, on ne TRANSFORME pas le string : on renvoie une version où les caractères sont transformées.

    <script>


var monTexte = "Bonjour";

monTexte.replace('o','i');

alert(monTexte);


monTexte = monTexte.replace('o','i');

alert(monTexte);


monTexte = "Bonjour";

monTexte = monTexte.replace('jour','soir');

alert(monTexte);


monTexte = monTexte.replace( /o/g , '*' );

alert(monTexte);


    </script>

Bonjour

Binjour

Bonsoir

B*ns*ir

indexOf : pour obtenir l'index de la première occurence trouvée dans le string. S'il ne trouve pas, il renvoie -1 : cela permet de vérifier que le string cherché n'est pas dans le string de base.

    <script>


var monTexte = "Bonjour";

alert( monTexte.indexOf('B') );

alert( monTexte.indexOf('o') );

alert( monTexte.indexOf('z') );


    </script>

0

1

-1

Voyons maintenant quelques autres méthodes pratiques pour comparer les chaînes de caractères ou les caractères entre eux :

Trois nouvelles méthodes des strings bien pratiques

La méthode toLowerCase :

maChaine.toLowerCase() renvoie maChaine en minuscule si maChaine est une lettre ou une chaîne.

    <script>


var monTexte = "Bonjour à TOUS";

alert( monTexte.toLowerCase() );

alert( monTexte );


monTexte = monTexte.toLowerCase();

alert( monTexte );


    </script>

bonjour à tous

Bonjour à TOUS

bonjour à tous

Pour MODIFIER monTexte, il faudrait donc noter monTexte = monTexte.toLowerCase().

La méthode toUpperCase :

maChaine.toUpperCase() renvoie maChaine en majuscule si maChaine est une lettre ou une chaîne.

    <script>


var monTexte = "Bonjour à TOUS";

alert( monTexte.toUpperCase() );

alert( monTexte );


monTexte = monTexte.toUpperCase();

alert( monTexte );


    </script>

BONJOUR À TOUS

Bonjour à TOUS

BONJOUR À TOUS

Pour MODIFIER monTexte, il faudrait donc noter monTexte = monTexte.toLowerCase().

La fonction isNaN : renvoie true si le string ne peut pas s'interpréter comme un nombre. Pratique pour savoir si l'entrée utilisateur est bonne ou non.

Exemple :

    <script>


var tableauTest = [ 'A', 'A3', '3A', '6E-2', 'Bonjour', '-3.6', '-3,6' ];

for ( element of tableauTest ) {

    console.log( element );

    console.log( isNaN(element) );

}


    </script>

Attention, nous sommes bien en logique inverse : on obtient true si ce n'est pas un nombre.

A : true (ce n'est pas un nombre)

A3 :true (ce n'est pas un nombre)

3A :true (ce n'est pas un nombre)

6E-2 : false (c'est un nombre)

Bonjour : true (ce n'est pas un nombre)

A3 :true (ce n'est pas un nombre)

-3.6 : false (c'est un nombre)

-3,6 :true (ce n'est pas un nombre)

On notera qu'on doit utiliser la notation anglosaxonne avec un point si on veut utiliser un nombre à virgule.

Voilà de quoi faire des tests :

Votre entrée x :

Réponse de la fonction isNaN(x) =

Réponse de la fonction parseFloat(x) =

Attention, la fonction isNaN observe l'intégralité de l'expression là où parseInt("12a") renvoie 12 car elle tente de trouver un nombre entier de la chaîne. Mais isNaN("12a") renvoie true car "12a" n'est pas un nombre !

Par contre, isNaN("12e-3") renvoie false car la fonction détecte qu'il s'agit de 12.10-3.

15° Améliorer le test du pendu en comparant non pas le vrai contenu de

Mais cette fois, il faudra comparer deux versions intégralement en majuscules ou en minuscules. Ainsi demandant A devrait revéler les a et les A. Et l'inverse devrait vrai aussi.

...CORRECTION ...

function verifier_presence() {


    var motSecret = "Bonjour tout le monde";

    var motMoinsSecret = document.getElementById("mot_moins_secret").innerHTML;


    // Création initiale de motMoinsSecret si vide initialement

    if ( motMoinsSecret == "" ) {

        for (var element of motSecret) {

            if (element == '-' || element == ' ') {

                motMoinsSecret = motMoinsSecret + element;

            } else {

                motMoinsSecret = motMoinsSecret + '*';

            }

        }

    }


    // Récupération de la lettre voulue

    var maLettre = document.getElementById("ma_lettre").value;


    maLettre = maLettre.toUpperCase();

    var motSecretMaj = motSecret.toUpperCase();

    if (motSecretMaj.indexOf(maLettre) >= 0) {

        // Création lettre par lettre de temporaire

        // Copie de motMoinsSecret à début : code à modifier

        var temporaire = "";

        for (var index in motMoinsSecret) {

            var dansMoinsSecret = motMoinsSecret.charAt(index);

            var dansSecret = motSecret.charAt(index);

            if ( maLettre == dansSecret.toUpperCase() ) {

                temporaire = temporaire + dansSecret;

            } else {

                temporaire = temporaire + dansMoinsSecret;

            }

        }


        // Modification de motMoinsSecret

        motMoinsSecret = temporaire;


        // Modification du texte dans le span mot_moins_secret

        document.getElementById("mot_moins_secret").innerHTML = motMoinsSecret;

    }

}

16° Finaliser enfin en gérant la fin. Il faudra rajouter un span pour indiquer qu'on a gagné !

Il est temps d'arrêter. Ce n'est pas encore parfait. On pourrait remplacer les é,è,ê par un simple e. Idem pour les à ... On pourrait compter les étapes ou même proposer de découvrir le mot avant la fin. Mais vous avez vu l'essentiel.

Pour voir plein d’autres choses qu’on peut faire avec les chaînes de caractères, le mieux est d’aller voir la documentation de javascript ou de lancer une recherche :

 DOCS Javascript SUR LES STRINGS (str) 

Une dernière chose néanmoins : vous risquez parfois de trouver des codes de détection avec des tildes :

Si vous désirez savoir si le caractère car est l'un des caractères contenus dans la chaine " ,;:!'()[]{}=+-*", vous pouvez utiliser ceci, avec le tilde :

if (~(" ,;:!'()[]{}=+-*".indexOf(car)))

Cela permet de détecter que car est l'un des caractères contenus dans " ,;:!'()[]{}=+-*".

Sans cette façon de faire, il faudra tester un à un les caractères recherchés avec des if et des else if. Un peu long.

Explication : le tilde est un opérateur qui agit sur les bits d'un octet. Notamment, il a pour spécificité de faire passer -1 à 0. Or 0 veut dire false. Donc si la méthode ne trouve pas le caractère, elle renvoit -1, qui est transformé en 0, qui est interprété en false lors du test if.

C'est concis, mais c'est obscur à comprendre si on ne le sait pas...

5 - Mise en pratique : des exercices (pas faciles !)

Pour stabiliser toutes ces nouvelles connaissances (dont le isNaN()), voilà trois exercices, plutôt longs. Faites au moins le premier.

Calcul du nombre de caractéres dans une phrase (difficulté moyenne):

17° Réalisons un exercice pratique : je voudrais distinguer les gens par le nombre moyen de caractères par phrase écrite. Je pense montrer que ce nombre moyen est corrélé avec la formation et la durée de la formation. Je voudrais donc pouvoir compter le nombre de lettres de la première phrase d’un texte. Penser à bien regarder les textes d'explication ci-dessous. Il faudra donc :

  1. Lire les caractères du texte un à un à l’aide d’une boucle for.
  2. Créer un compteur qui va s’incrémenter de 1 si le caractère est bien une lettre (voir la fonction isNaN présentée ci-dessus, mais il faudra également supprimer les virgules et autres point-virgules, point d'interrogation...) .
  3. Sortir de la boucle for dès qu’on rencontre un point (on ne doit pas en tenir compte pour la longueur de la phrase) : il faudrait utiliser un break. Allez voir la FAQ.
  4. Afficher le nombre de lettres (alphabétiques donc) rencontrées sur la première phrase.

Comme il faut compter les caractères, on peut aussi supprimer les caractères de ponctuation (sauf le point qui nous servira d'arrêt). Deux méthodes possibles sont proposés ici :

Pour savoir si un caractère doit être comptabilisé ou non, on peut utiliser isNaN pour supprimer les chiffres et if (~(" ,;:!'()[]{}=+-*".indexOf(car))) permet de détecter que car est l'un des caractères contenus dans " ,;:!'()[]{}=+-*".

Sinon, on peut tout simplement tenter de supprimer tous les caractères non voulus en utilisant

monTexte = monTexte.replace( / ?'?"?,?;?:?-?_?\??/g , '');

On utilise une expression régulière dans le replace. Ainsi

  • L'expression régulière est donnée entre les deux /
  • Le g après l'expression régulière signifie que la recherche est globale
  • Les ? dans / ?'?"? ect ... signifient que le caractère (espace, ' ou " ...) peut apparaitre 1 fois ou 0 fois
  • \? indique qu'on recherche un point d'interrogation et qu'il ne s'agit pas du ? indiquant le nombre de recherche (0 ou 1 fois)
  • \?? indique donc qu'on cherche ? et qu'il peut apparaître 0 ou 1 fois.

Comme vous le voyez, l'utilisation des expressions régulières nécessite de connaitre une sorte de langage dans le langage. La documentation est néanmoins assez bien fournie. Nous compléterons plus tard son utilisation. Pour l'instant, je ne fais que vous la présenter.

Tester le programme avec :

monTexte = "Voilà la première phrase, celle dans laquelle il faut compter les caractères, enfin sauf les virgules, les espaces et les points. On ne devrait pas compter non plus les ; mais comme c'est une seconde phrase, cela n'a pas d'importance."

Vous devriez trouver 105 caractères. Pas facile, si ?

Le ROT-13 (plus compliqué) :

Mini-projet 2 : nous allons maintenant manipuler les caractères via leurs codes ASCII.

Voilà l'affichage non fonctionnel :

Le texte à coder svp :

Le texte encodé :

Le cryptage ROT-13 : c’est un codage primitif, datant de l’époque où les calculateurs automatiques n’existaient pas. On décale toutes les lettres de 13 lettres. Comme l’alphabet en comporte 26, l’avantage est que le codage et le décodage nécessite le même algorithme.

A devient N, N devient A.

B devient O, O devient B.

M devient Z, Z devient M.

Ceci en majuscule, comme en minuscule.

Le rot13

Voilà un bout de code qui va vous permettre :

  • de créer un input-texte d'id "texte_de_base"dans lequel rentrer votre texte.
  • de créer un bouton permet d’activer la fonction rot13.
  • de créer un span d'id "texte_encode" dans lequel on affichera le texte codé ou décodé après application de ROT-13.

<div class="test_html">

    <p>Le texte à coder svp :<input type="text" id="texte_de_base" placeholder="Votre texte" size="75"></p>

    <p><input type="button" onclick="rot13()" value="Activer le rot13"></p>

    <p>Le texte encodé : <span id="texte_encode"></span></p>

</div>

Le caractère A correspond au code 65 et Z correspond au code 90 lorsqu'on utilise respectivement "A".charCodeAt(0) et "Z".charCodeAt(0).

A l'inverse, String.fromCharCode(65) renvoie A et String.fromCharCode(90) renvoie Z.

ATTENTION : vous n'aurez qu'à modifier le contenu de la fonction rot13. Inutile de chercher à modifier le code ailleurs pour répondre aux questions.

function rot13() {


    // Récupération du texte à encoder

    var texteDeBase = document.getElementById("texte_de_base").value;


    // Création du texte encodé

    var texteEncode = texteDeBase;


    // Placement du texte encodé à l'écran

    document.getElementById("texte_encode").innerHTML = texteEncode;


}

18° Modifier et compléter la fonction rot13 donnée de façon à :

  • lire avec charCodeAt le code ASCII de chaque caractère du texte stocké dans texteDeBase
  • rajouter 13 à ce code
  • trouver avec fromCharCode le nouveau caractère à afficher.
  • recréer le nouveau texte texteEncode à l’aide des nouveaux caractères.

Votre code devrait vous permettre de gérer parfaitement l'encodage de A à M. Mais pas encore de N à Z !

Testez votre interface : si vous rentrez  ABC , elle devrait vous répondre  NOP . Idem en minuscule.

...CORRECTION ...

function rot13() {


    // Récupération du texte à encoder

    var texteDeBase = document.getElementById("texte_de_base").value;


    // Création du texte encodé

    var texteEncode = "";

    for ( var caractere of texteDeBase ) {

        var codeObtenu = caractere.charCodeAt(0);

        codeObtenu = codeObtenu + 13 ;

        nouveauCaractere = String.fromCharCode(codeObtenu) ;

        texteEncode = texteEncode +nouveauCaractere

    }


    // Placement du texte encodé à l'écran

    document.getElementById("texte_encode").innerHTML = texteEncode;


}

Le problème du caractère  N  ? Il renvoie  [  et pas  A . Pourquoi ?

C'est simple : "N".charCodeAt(0) renvoie le code 78. 78+13 vaut 91. Or String.fromCharCode(91) est bien le caractère après le Z : cela renvoie  [ .

TypeDécimalSymboleBinaire
Printable65A100 0001
Printable66B100 0010
......... ....
Printable77M100 1101
Printable78N100 1110
Printable79O100 1111
......... ....
Printable90Z101 1010
Printable91[101 1011
Printable92\101 1100

19° Gérer le code pour qu'on ne puisse pas :

  • dépasser 90(Z) : on doit continuer sur 65(A) et plus
  • dépasser 122(z) : on doit continuer sur 97(a) et plus

Testez votre interface avec  ABCNOP , elle devrait vous répondre  NOPABC . Idem en majuscule.

...CORRECTION ...

function rot13() {


    // Récupération du texte à encoder

    var texteDeBase = document.getElementById("texte_de_base").value;


    // Création du texte encodé

    var texteEncode = "";

    for ( var caractere of texteDeBase ) {

        var codeObtenu = caractere.charCodeAt(0);

        codeObtenu = codeObtenu + 13 ;

        if ( codeObtenu > 90 || codeObtenu > 122 ) {

            codeObtenu = codeObtenu - 26;

        }

        nouveauCaractere = String.fromCharCode(codeObtenu) ;

        texteEncode = texteEncode +nouveauCaractere

    }


    // Placement du texte encodé à l'écran

    document.getElementById("texte_encode").innerHTML = texteEncode;


}

Dernière amélioration pour aujourd'hui :  ABC NOP  devrait vous donner  NOP-ABC . Et oui : 32 est le code de l'espace et 32+13 = 45 est le code du tiret.

20° Rajouter un filtre sur la lecture et la modification du code du caractère : on ne doit rien faire si le code de base n'est pas celui de A à Z ou de a à z.

Testez votre code :  ABC NOP  devrait maintenant vous donner  NOP ABC . Ouf.

Si vous avez besoin d'aide, n'hésitez à me contacter. Il suffit de quelques tests bien placés.

6 - FAQ

Question : Comment lire rapidement des éléments précis d'un string ?

La lecture rapide des caractères d'un string

Pour finir, nous allons voir qu'on peut également simplifier l'extraction d'une partie d'un string à l'aide de méthode adaptée.

Pour lire le contenu des cases de 2 à 4, on peut écrire un for de type for (var i=2,i<5;i++) ou alors utiliser la méthode slice(2,5) :

var laChaine = "C'est bientôt la fin de cette partie";

var res = laChaine.slice(2, 5);

console.log(res);

On obtient

est

Si on donne des arguments négatifs, cela veut dire qu'on part de la fin de la chaine. Ainsi, slice(-3,-1) veut dire qu'on lit du caractère -3 jusqu'à atteindre en l'excluant le caractère -1 (la fin de chaîne). Attention donc, 0 reste le premier caractère de la chaine et -1 le dernier caractère. Il y a une raison logique interne à cela mais c'est vrai que ce n'est pas très pratique à utiliser au départ.

var laChaine = "C'est bientôt la fin de cette partie";

var res = laChaine.slice(-3, -1);

console.log(res);

On obtient

ti

Pour lire un contenu de longueur donnée (par exemple 3 caractères) à partir d'une position donnée (par exemple l'index 2), on peut utiliser la méthode substr(2,3) :

var laChaine = "C'est bientôt la fin de cette partie";

var res = laChaine.substr(2, 3);

console.log(res);

On obtient

est

Si cela dépasse, on ne crée pas d'erreur. La méthode s'arrête juste à la fin de la chaîne. Par contre, cette méthode ne supporte pas les indexations négatives comme la précédente.

Pour finir, on retiendra qu'il ne faut pas tenter de trouver le caractère précis d'un string x avec une instruction du type x[position] mais plutôt x.charAt(position). Pourquoi ? Et bien simplement car les strings et les arrays ne fonctionnent pas exactement de la même façon dans certains cas précis et qu'écrire x[position] pourrait laisser croire que x est un Array (alors que c'est une String).

Question : comment sortir d'une boucle for avant la fin ?

On utilise l'instruction break. Exemple : on lance une boucle avec un compteur évoluant de 0 à 20 mais on sort définitivement de la boucle lorsque le compteur vaut 5 :

function bouclage() {

    alert("DEBUT DU BOUCLAGE : ouvrez la console pour voir les valeurs calculées.");

    for (var compteur=0;compteur<11;compteur++) {

        alert(compteur);

        if (compteur == 5) {

            break;

        }

    }

    alert("FIN DU BOUCLAGE");

}

CLIQUEZ SUR UN BOUTON-REPONSE :

compteur :

Sur la console, on voit bien qu'on ne revient pas dans la boucle pour le cas 6 et plus :

0

1

2

3

4

5

Remarque : même s'il reste des instructions sous le break, on ne les exécute pas : on quitte immédiatement la boucle.

Question : Je voudrais compter de 0 à 6 mais sans passer par 5, c'est possible ?

On utilise l'instruction continue. On lance une boucle avec un compteur évoluant de 0 à 6 mais sans agir pour 5 en forçant un retour au début de la boucle :

function bouclage() {

    alert("DEBUT DU BOUCLAGE : ouvrez la console pour voir les valeurs calculées.");

    for (var compteur=0;compteur<7;compteur++) {

        if (compteur == 5) {

            continue;

        }

        alert(compteur);

    }

    alert("FIN DU BOUCLAGE");

}

CLIQUEZ SUR UN BOUTON-REPONSE :

compteur :

Comme vous le voyez, je n'ai même pas besoin de mettre un else après le if. L'exécution du programme va répartir au début de la boucle dès qu'elle rencontrera continue.

Sur la console, on voit bien qu'on a sauté l'exécution du print pour le 5 :

0

1

2

3

4

6

Remarque : même s'il reste des instructions sous le continue, on ne les exécute pas : on remonte immédiatement la boucle pour faire l'itération suivante.

Voilà. Comme pour les images, ceux qui voudront travailler sur un projet lié à la gestion de texte auront l’occasion d’aller beaucoup plus loin. Par contre, sachez que Javascript n'est pas particulièrement adapté à la gestion d'énormes strings. Si vous voulez analyser "Les racines du mal" ou autres romans, autant utiliser un langage comme C++ ou Python.