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.
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.
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émoire | A l’écran | Valeur codante | en binaire |
---|---|---|---|
Start of Text | 2 | 000 0010 | |
H | H | 72 | 1001000 |
e | e | 101 | 1100101 |
l | l | 108 | 1101100 |
l | l | 108 | 1101100 |
o | o | 111 | 1101111 |
Space | 32 | 100000 | |
W | W | 87 | 1010111 |
o | o | 111 | 1101111 |
r | r | 114 | 1110010 |
l | l | 108 | 1101100 |
d | d | 100 | 1100100 |
End of Text | 3 | 000 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 :
Type | Décimal | Symbole | Signification | Binaire |
---|---|---|---|---|
Contrôle | 0 | NULL or NUL | Null | 000 0000 |
Contrôle | 1 | SOM or SOH | Start of Heading | 000 0001 |
Contrôle | 2 | EOA or STX | Start of Text | 000 0010 |
Contrôle | 3 | EOM or ETX | End of Text | 000 0011 |
Contrôle | 4 | EOT | End of Transmission | 000 0100 |
Contrôle | 5 | WRU or ENQ | Enquiry | 000 0101 |
Contrôle | 6 | RU or ACK | Acknowledgement | 000 0110 |
Contrôle | 7 | BELL or BEL | Bell | 000 0111 |
Contrôle | 8 | FE0 or BS | Backspace | 000 1000 |
Contrôle | 9 | HT/SK or HT | Horizontal Tab | 000 1001 |
Contrôle | 10 | LF | Line Feed | 000 1010 |
Contrôle | 11 | VTAB or VT | Vertical Tab | 000 1011 |
Contrôle | 12 | FF | Form Feed | 000 1100 |
Contrôle | 13 | CR | Carriage Return | 000 1101 |
Contrôle | 14 | SO | Shift Out | 000 1110 |
Contrôle | 15 | SI | Shift In | 000 1111 |
Contrôle | 16 | DC0 or DLE | Data Link Escape | 001 0000 |
Contrôle | 17 | DC1 | Device Control 1 | 001 0001 |
Contrôle | 18 | DC2 | Device Control 2 | 001 0010 |
Contrôle | 19 | DC3 | Device Control 3 | 001 0011 |
Contrôle | 20 | DC4 | Device Control 4 | 001 0100 |
Contrôle | 21 | ERR or NAK | Negative Acknowledgement | 001 0101 |
Contrôle | 22 | SYNC or SYN | Synchronous Idle | 001 0110 |
Contrôle | 23 | LEM or ETB | End of Transmission Block | 001 0111 |
Contrôle | 24 | S0 or CAN | Cancel | 001 1000 |
Contrôle | 25 | S1 or EM | End of Medium | 001 1001 |
Contrôle | 26 | S2 or SS | Substitute | 001 1010 |
Contrôle | 27 | S3 or ESC | Escape | 001 1011 |
Contrôle | 28 | S4 or FS | File Separator | 001 1100 |
Contrôle | 29 | S5 or GS | Group Separator | 001 1101 |
Contrôle | 30 | S6 or RS | Record Separator | 001 1110 |
Contrôle | 31 | S7 or US | Unit Separator | 001 1111 |
Et à partir de 32, on trouve les caractères dits imprimables car visibles à l'écran :
Type | Décimal | Symbole | Signification | Binaire |
---|---|---|---|---|
Printable | 32 | (space) | (space) | 010 0000 |
L'espace est un caractère un peu spécial mais les autres sont visibles :
Type | Décimal | Symbole | Binaire |
---|---|---|---|
Printable | 33 | ! | 010 0001 |
Printable | 34 | " | 010 0010 |
Printable | 35 | # | 010 0011 |
Printable | 36 | $ | 010 0100 |
Printable | 37 | % | 010 0101 |
Printable | 38 | & | 010 0110 |
Printable | 39 | ' | 010 0111 |
Printable | 40 | ( | 010 1000 |
Printable | 41 | ) | 010 1001 |
Printable | 42 | * | 010 1010 |
Printable | 43 | + | 010 1011 |
Printable | 44 | , | 010 1100 |
Printable | 45 | - | 010 1101 |
Printable | 46 | . | 010 1110 |
Printable | 47 | / | 010 1111 |
Printable | 48 | 0 | 011 0000 |
Printable | 49 | 1 | 011 0001 |
Printable | 50 | 2 | 011 0010 |
Printable | 51 | 3 | 011 0011 |
Printable | 52 | 4 | 011 0100 |
Printable | 53 | 5 | 011 0101 |
Printable | 54 | 6 | 011 0110 |
Printable | 55 | 7 | 011 0111 |
Printable | 56 | 8 | 011 1000 |
Printable | 57 | 9 | 011 1001 |
Printable | 58 | : | 011 1010 |
Printable | 59 | ; | 011 1011 |
Printable | 60 | < | 011 1100 |
Printable | 61 | = | 011 1101 |
Printable | 62 | > | 011 1110 |
Printable | 63 | ? | 011 1111 |
Printable | 64 | @ | 100 0000 |
Printable | 65 | A | 100 0001 |
Printable | 66 | B | 100 0010 |
Printable | 67 | C | 100 0011 |
Printable | 68 | D | 100 0100 |
Printable | 69 | E | 100 0101 |
Printable | 70 | F | 100 0110 |
Printable | 71 | G | 100 0111 |
Printable | 72 | H | 100 1000 |
Printable | 73 | I | 100 1001 |
Printable | 74 | J | 100 1010 |
Printable | 75 | K | 100 1011 |
Printable | 76 | L | 100 1100 |
Printable | 77 | M | 100 1101 |
Printable | 78 | N | 100 1110 |
Printable | 79 | O | 100 1111 |
Printable | 80 | P | 101 0000 |
Printable | 81 | Q | 101 0001 |
Printable | 82 | R | 101 0010 |
Printable | 83 | S | 101 0011 |
Printable | 84 | T | 101 0100 |
Printable | 85 | U | 101 0101 |
Printable | 86 | V | 101 0110 |
Printable | 87 | W | 101 0111 |
Printable | 88 | X | 101 1000 |
Printable | 89 | Y | 101 1001 |
Printable | 90 | Z | 101 1010 |
Printable | 91 | [ | 101 1011 |
Printable | 92 | \ | 101 1100 |
Printable | 93 | ] | 101 1101 |
Printable | 94 | ^ | 101 1110 |
Printable | 95 | _ | 101 1111 |
Printable | 96 | ` | 110 0000 |
Printable | 97 | a | 110 0001 |
Printable | 98 | b | 110 0010 |
Printable | 99 | c | 110 0011 |
Printable | 100 | d | 110 0100 |
Printable | 101 | e | 110 0101 |
Printable | 102 | f | 110 0110 |
Printable | 103 | g | 110 0111 |
Printable | 104 | h | 110 1000 |
Printable | 105 | i | 110 1001 |
Printable | 106 | j | 110 1010 |
Printable | 107 | k | 110 1011 |
Printable | 108 | l | 110 1100 |
Printable | 109 | m | 110 1101 |
Printable | 110 | n | 110 1110 |
Printable | 111 | o | 110 1111 |
Printable | 112 | p | 111 0000 |
Printable | 113 | q | 111 0001 |
Printable | 114 | r | 111 0010 |
Printable | 115 | s | 111 0011 |
Printable | 116 | t | 111 0100 |
Printable | 117 | u | 111 0101 |
Printable | 118 | v | 111 0110 |
Printable | 119 | w | 111 0111 |
Printable | 120 | x | 111 1000 |
Printable | 121 | y | 111 1001 |
Printable | 122 | z | 111 1010 |
Printable | 123 | { | 111 1011 |
Printable | 124 | | | 111 1100 |
Printable | 125 | } | 111 1101 |
Printable | 126 | ~ | 111 1110 |
Le numéro 127 correspond à la touche DEL d'effacement :
Type | Décimal | Symbole | Signification | Binaire |
---|---|---|---|---|
Contrôle | 127 | DEL | 111 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) :
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
Créer une chaîne de caractères, c’est facile en javascript :
monTexte = "Hello World !";
monTexte = "Hello" + " World" + "!";
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']
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']
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.
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.
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 :
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
maLettre
etmotSecret[ index ]
ou motSecret.charAt(index)
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...
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 :
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
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.
Voilà un bout de code qui va vous permettre :
<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 à :
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 [
.
Type | Décimal | Symbole | Binaire |
---|---|---|---|
Printable | 65 | A | 100 0001 |
Printable | 66 | B | 100 0010 |
... | .. | . | ... .... |
Printable | 77 | M | 100 1101 |
Printable | 78 | N | 100 1110 |
Printable | 79 | O | 100 1111 |
... | .. | . | ... .... |
Printable | 90 | Z | 101 1010 |
Printable | 91 | [ | 101 1011 |
Printable | 92 | \ | 101 1100 |
19° Gérer le code pour qu'on ne puisse pas :
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.
Question : Comment lire rapidement des éléments précis 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.