Introduction à la programmation en Java Faculté des Sciences de Nice Licence Math-Info 2006-2007 COURS 9 Les chaînes de caractères et le codage Module L1I1 Frédéric MALLET Jean-Paul ROY 9-1 9-2 Les caractères Ascii (Norme ISO 646 : 7 bits) Lettre alphabet, chiffres, ponctuation, contrôle Code Ascii (7 bits + 1 bit de parité) American Standard Code for Information Interchange permet 128 combinaisons différentes : 2 7 Un code est associé à chaque caractère Exemples : Le caractère 'A' est codé 65 : 0x41 Le caractère '0' est codé 48 : 0x30 Le code 97 correspond au caractère 'a' Le code 10 correspond au retour à la ligne 9-3 American Standard Code for Information Interchange 'G' est codé 47h soit 0100 0111 2 9-4
Les caractères (codés sur 8 bits) Code iso-latin-1 (8 bits) Code EBCDIC Extended Binary Coded Decimal Interchange Code Utilisé principalement par IBM Code Ansi American National Standard Institute De 0 à 127 : Code Ascii inchangé De 128 à 255 : extensions multilingues Connu sous les noms ISO-latin-1, ISO-8859-1 Pas suffisant pour toutes les langues 10 000 Kanji du japonais, alphabets cyrillique, hébreu, arabe,... 9-5 'G' est codé 47h soit 0100 0111 2 9-6 Le type char de Java et l'unicode Code les caractères sur 16 bits (2 octets) Les caractères les plus utilisés sont codés sur 8 bits (code ASCII) Les autres caractères sont codés sur 16 bits En Java, L'apostrophe différentie les caractères et les variables '\uxxxx' représente le caractère d'unicode XXXX Exemples : char c; c = 'a'; // la variable c devient égale à 'a' c = '\u00e9'; // affectée au caractère d'unicode 00e9 : é 9-7 Conversion char int Pour obtenir l Unicode d un caractère char c ; int code ; code = (int)c; Pour connaître le caractère associé à un Unicode c = '\u0061'; // caractère d unicode 0x61 : 'a' code = 65; c = (char)code; // caractère d unicode 0x65 : 'e' code = 0x20AC; c = (char)code; // caractère d unicode 0x20AC : ' ' Le transtypage est permis car char est isomorphe à int 9-8
L opérateur + et les caractères Les opérateurs de comparaison On a déjà vu que l opérateur + a plusieurs significations suivant le type des opérandes Addition sur les nombres Concaténation sur les chaînes de caractères Pour les caractères : 'a'+2, 'a'+'b'? Le caractère est remplacé par son code unicode (1 entier int) 'a'+2 99 (car 'a' a le code 97) 'a'+'b' 195 (car 'b' a le code 98) Que vaut 'c'-'a'? Que vaut 'A'+2? Que vaut 'c'-'a'+'a'? 2 Le code de 'C' Le code de 'C' 9-9 L égalité : opérateurs == et!= La comparaison : opérateurs >, >=, < et <= Ces opérateurs fonctionnent sur les int Lorsqu ils sont utilisés avec des caractères, le caractère est remplacé par son code Unicode Exemple : 'a' == 97 true 'C' > 'A' true 97 < 'z' true Comment faire pour savoir si un caractère représente une minuscule? Une majuscule? 9-10 La classe Character Notes personnelles Le caractère c est-il une majuscule? c>='a' && c<='z' Ça marche pour les 26 lettres standards Et pour les caractères spéciaux : Ç, É,? La classe Character propose des méthodes statiques qui rendent indépendant du jeu de caractères : Character.isUpperCase('\u00C7') true Ç Character.isLowerCase('a') true Character.isLetter('$') false Character.isDigit('8') true 9-11 9-12
La classe String Les chaînes de caractères 9-13 La classe String C est une collection ordonnée de caractères (char) Le premier caractère a l indice 0 L implémentation exacte n est pas connue Les méthodes couramment utilisées int length() // la longueur de la chaîne char charat(int i) // caractère d indice i int indexof(char c) // position du caractère c (ou 1) int indexof(string ch) // position de ch (ou -1) String tolowercase() // nouvelle chaîne avec le même // contenu tout en minuscule String touppercase() // nouvelle chaîne avec le même // contenu tout en majuscule 9-14 Les String sont inaltérables String char[ ] String Aucune méthode ne permet de modifier le contenu d une chaîne de caractères pas de setcharat(char c, int i) On peut y arriver par concaténation de sous-chaînes String s ; s = s.substring(0,i) + c + s.substring(i); s.substring(a, b) extrait une sous-chaîne de s du caractère d indice a au caractère d indice b-1 Les méthodes touppercase, tolowercase, replace qui semblent modifier les chaînes construisent en réalité un nouvel objet à chaque fois. 9-15 Pour modifier une chaîne de caractères, il est plus efficace de la transformer en tableau de caractères char[] tochararray() de la classe String Exemple : String s; char[] tab = s.tochararray(); tab[i] = 'c'; Après les modifications, on peut construire un nouveau String à partir du tableau de caractères char[] data = { 'a', 'b', 'c', 'd', 'e' ; s = new String(data); s = new String(data, 1, 3); s devient "abcde" s devient "bcd" 9-16
Exemple La méthode replace() /** Remplacer dans une chaîne toutes les occurrences du caractère cold par le * caractère cnew. * @param s la chaîne à modifier * @param cold le caractère à remplacer * @param cnew le caractère par lequel on remplace * @return une nouvelle chaîne modifiée */ static String replace(string s, char cold, char cnew) { char[] tmp; tmp = s.tochararray(); for(int i=0; i < tmp.length; i+=1) if(tmp[i] == cold) tmp[i] = cnew ; return new String(tmp); 9-17 Attention RAPPEL : Les tableaux ont une taille constante déterminée à la construction Passer par un tableau de char est très pratique mais ne permet de faire que des transformations à taille constante Pour faire des modifications à taille variable, il FAUT créer un autre tableau et recopier tous les caractères { char[] t1 = s.tochararray(); char[] t2 = new char[t1.length+2];//(par exemple) for(int i=0; i<t1.length; i+=1) t2[i]=t1[i]; return new String(t2); 9-18 Une méthode concat() Notes personnelles /** concatène les chaînes passées en paramètre dans un tableau * @param tableau chaînes à concaténer * @return chaîne composée de chaque mot de tableau * séparés par un espace */ static String concat(string[] tableau) { String res = ""; // chaîne vide for(int i = 0; i < tableau.length; i+=1) { res += tableau[i] ; res += ' '; return res; 9-19 9-20
res = res + tableau[i] : complexité? Les String sont inaltérables : il faut recréer une nouvelle chaîne à chaque tour de boucle char[] restmp ; // quelle est la longueur du résultat? // res.length() + tableau[i].length() restmp = new char[res.length() + tableau[i].length()]; // copie de l ancien res dans restmp for(int i=0; i<res.length(); i+=1) restmp[i] = res.charat(i); // copie de tableau[i] à la suite O( tableau[i].length() ) for(int j=0; j<tableau[i].length(); j+=1) restmp[res.length() + j] = tableau[i].charat(j); res = new String(resTmp); O( res.length() + tableau[i].length() ) O( res.length() ) // construit le résultat 9-21 concat() : une autre version /** concatène les chaînes passées en paramètre dans un tableau * @param tableau chaînes à concaténer * @return chaîne composée de chaque mot de tableau * séparés par un espace */ static String concat(string[] tableau) { char[] res = new char[100]; // 100 caractères au plus int indres = 0; // position dans le résultat for(int i = 0; i < tableau.length; i+=1) { for(int j=0; j < tableau[i].length; j+=1) { res[indres] = tableau[i].charat(j); indres += 1; res[indres] = ' '; // ajoute un espace entre les mots indres += 1; return new String(res, 0, indres); 9-22 Si le tampon res est suffisamment grand, il suffit de copier tous les caractères de tableau[i] à la suite Pas de réallocation On peut toujours faire un premier tour pour calculer la longueur totale du résultat Sinon, il faudrait agrandir la taille de res et recopier tout ce qui a déjà été fait 9-23 Bilan sur la boucle complète Avec la concaténation '+' Premier tour : O( 0 + tableau[0].length() ) = c 0 Deuxième tour : O (c 0 + tableau[1].length() ) = c 1 Troisième tour : O (c 1 + tableau[1].length() ) = c 2 Au total : c 0 + c 1 + c 2 + + c n = t 0 + (t 0 +t 1 ) + (t 0 +t 1 +t 2 ) + (t 0 +t 1 +t 2 +t 3 ) + Avec un tableau de char Dans le pire des cas : idem Dans le meilleur des cas, au total: t 0 + t 1 + t 2 + t 3 + + t n Il suffit que le tampon soit suffisamment grand pour atteindre le meilleur des cas 9-24
Application codage de César On veut réaliser une classe qui permet le codage et le décodage de chaînes de caractères pas lisible par tout le monde Notre algorithme de codage consiste à utiliser une clé privée : un entier nommé clé Cette clé est choisie par le codeur Seul l objet de codage la connaît Chaque objet de codage peut avoir une clé différente Démonstration 9-25 9-26 Algorithme de codage Chaque caractère majuscule de la chaîne d origine est remplacé par le caractère suivant dans l alphabet 'A' est remplacé par 'B',, 'Z' est remplacé par 'A' En fait la distance entre le caractère codé et le caractère original est déterminé par la clé : ici la clé vaut 1 Si la clé vaut 2: 'A' est remplacé par 'C', La méthode code String code(string chaine) { char[] buf = chaine.tochararray(); // pour chaque caractère for(int i=0; i<chaine.length(); i+=1) buf[i] = this.codechar(buf[i]); return new String(buf); Notes personnelles 9-27 9-28
Le décodage Il suffit de décaler dans l autre sens /** décode une chaîne suivant le code de César * @param chaîne à décoder */ String decode(string chaine) { char[] buf = chaine.tochararray(); // pour chaque caractère for(int i=0; i<chaine.length(); i+=1) { buf[i] = this.decodechar(buf[i]); return new String(buf); 9-29 Le codage de chaque caractère c Calculer la position dans l alphabet Décaler de clé [c n est pas une majuscule] [c est une majuscule] S assurer que c est toujours une lettre codechar(char) c c est inchangé 9-30 La méthode codechar() La méthode decodechar() char codechar(char c) { if (Character.isUpperCase(c)) { // calcule la position dans l alphabet // (à partir de la fin : pos>=0) int pos = c - 'A'; // décale de clé position sur la droite pos = pos + this.cle ; // s assure qu on ne déborde pas pos = pos % 26 ; c = (char)('a' + pos) ; return c; 9-31 char decodechar(char c) { if (Character.isUpperCase(c)) { // calcule la position dans l alphabet // (à partir de la fin : pos>=0) int pos = 'Z' - c; // décale de clé position sur la gauche pos = pos + this.cle ; // s assure qu on ne déborde pas pos = pos % 26 ; c = (char)('z'- pos) ; return c; 9-32
code() et codechar() La méthode code : Codeur. String String La méthode codechar : Codeur. char char Surcharge : On a le droit d utiliser l identificateur code pour nommer les deux méthodes On dit qu on surcharge la méthode code() char code(char c) { if (Character.isUpperCase(c)) { // calcule la position dans l alphabet int pos = ((c-'a') + this.cle) % 26; c = (char)('a' + pos) ; return c; Résoudre la surcharge Comment faire la différence entre code(string) et code(char)? Dans BlueJ : Il suffit de choisir la bonne! Lors de l écriture de code Java (exemple code(string)) L ambiguïté est levée par le type et le nombre des arguments String code(string chaine) { char[] buf = chaine.tochararray(); for(int i=0; i<chaine.length(); i+=1) buf[i] = this.code(buf[i])); return new String(buf); buf[i] est de type char, on n utilise donc la méthode code avec UN 9-33 paramètre de type char 9-34 Codage par substitution Avec un ordinateur moderne, on peut facilement essayer toutes les clés pour trouver décoder un message Le codage par substitution utilise une chaîne de 26 caractères comme clé privée Chaque lettre de la clé correspond à une lettre de l alphabet Exemple : clé = "LEZBUOITAVDMNPHYRCFGJKQSWX"; Tous les 'A' sont substitués par 'L', tous les 'B' par 'E', etc. Ainsi, "BONJOUR" est codé "EHPVHJC" Il y a 26! clés possibles Notes personnelles On utilise les statistiques pour casser ce code! 9-35 9-36
Codage par substitution - décodage Pour décoder, on cherche la position de chaque caractère dans la clé pour déduire la lettre originale Exemple : clé = "LEZBUOITAVDMNPHYRCFGJKQSWX"; On veut décoder "EHPVHJC" Le 'E' est en deuxième position dans la clé, il correspond donc à la lettre 'B' Le 'C' est en position 18 dans la clé, il correspond à la lettre 'R' En Java : On utilise la méthode indexof(char) de la classe String pour trouver la position d un caractère dans la clé! 9-37 Codage de Vigenère La clé privée est une chaîne de caractères Chaque caractère de la clé sert de clé de «César» Exemple : clé = "ZYGOMATIQUE" 'Z' les lettres 1, 12, 23, 34 sont décalées de 25 ('Z' - 'A') 'Y' les lettres 2, 13, 24, 35 sont décalées de 24 ('Y' - 'A') 'G' les lettres 3, 14, 25, 36 sont décalées de 6 ('G' - 'A') 'O' les lettres 4, 15, 26, 37 sont décalées de 14 ('O' - 'A') "LE RIRE EST LE PROPRE DE L HOMME" devient "KC FURX UMX JK BRHXHY CC Z HHUCY" 9-38 Codage de Vigenère - décodage Comme pour le codage de César, pour décoder, il suffit de décaler dans l autre sens en utilisant la clé privée Combien y a-t-il de clés possibles? Table de codage ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ A ABCDEFGHIJKLMNOPQRSTUVWXYZ N NOPQRSTUVWXYZABCDEFGHIJKLM B BCDEFGHIJKLMNOPQRSTUVWXYZA O OPQRSTUVWXYZABCDEFGHIJKLMN C CDEFGHIJKLMNOPQRSTUVWXYZAB P PQRSTUVWXYZABCDEFGHIJKLMNO D DEFGHIJKLMNOPQRSTUVWXYZABC Q QRSTUVWXYZABCDEFGHIJKLMNOP E EFGHIJKLMNOPQRSTUVWXYZABCD R RSTUVWXYZABCDEFGHIJKLMNOPQ F FGHIJKLMNOPQRSTUVWXYZABCDE S STUVWXYZABCDEFGHIJKLMNOPQR G GHIJKLMNOPQRSTUVWXYZABCDEF T TUVWXYZABCDEFGHIJKLMNOPQRS H HIJKLMNOPQRSTUVWXYZABCDEFG U UVWXYZABCDEFGHIJKLMNOPQRST I IJKLMNOPQRSTUVWXYZABCDEFGH V VWXYZABCDEFGHIJKLMNOPQRSTU J JKLMNOPQRSTUVWXYZABCDEFGHI W WXYZABCDEFGHIJKLMNOPQRSTUV K KLMNOPQRSTUVWXYZABCDEFGHIJ X XYZABCDEFGHIJKLMNOPQRSTUVW L LMNOPQRSTUVWXYZABCDEFGHIJK Y YZABCDEFGHIJKLMNOPQRSTUVWX M MNOPQRSTUVWXYZABCDEFGHIJKL Z ZABCDEFGHIJKLMNOPQRSTUVWXY 9-39 Remarque sur ces codages On peut étendre l algorithme aux caractères Ascii ou même aux caractères unicode Si le pirate dispose de l algorithme de codage, le code est plus vulnérable Exemple : Algorithme de Vigenère Je code "AAAAAAA" avec la clé privée "ROUGE" On obtient "ROUGERO" 9-40