Introduction à la programmation en Java Les chaînes de caractères

Transcription

Introduction à la programmation en Java Les chaînes de caractères
COURS 9
Introduction à la
programmation en Java
Les chaînes de caractères
Faculté des Sciences de Nice
et le codage
Licence Math-Info 2006-2007
Module L1I1
Frédéric MALLET
Jean-Paul ROY
9-1
Les caractères
Ascii (Norme ISO 646 : 7 bits)
♦ Lettre alphabet, chiffres, ponctuation, contrôle
♦ Code Ascii (7 bits + 1 bit de parité)
9-2
American Standard Code for Information Interchange
American Standard Code for Information Interchange
permet 128 combinaisons différentes : 27
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
'G' est codé 47h soit 0100 01112
9-3
9-4
Code iso-latin-1 (8 bits)
Les caractères (codés sur 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, ...
'G' est codé 47h soit 0100 01112
9-5
9-6
Conversion char ⇔ int
Le type char de Java et l'Unicode
♦ Pour obtenir l’Unicode d’un caractère
♦ Code les caractères sur 16 bits (2 octets)
char c ;
int code ;
code = (int)c;
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,
♦ Pour connaître le caractère associé à un Unicode
L'apostrophe différentie les caractères et les variables
'\uXXXX' représente le caractère d'Unicode XXXX
♦ Exemples :
char c;
// la variable c devient égale à 'a'
c = '\u00e9'; // affectée au caractère d'Unicode 00e9 : é
c = 'a';
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-7
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
♦ 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 :
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)
'a' == 97
'C' > 'A'
97 < 'z'
♦ Que vaut 'c'-'a' ?
2
Le code de 'C'
♦ Que vaut 'A'+2 ?
♦ Que vaut 'c'-'a'+'A' ? Le code de 'C'
→
→
→
true
true
true
♦ Comment faire pour savoir si un caractère
représente une minuscule ? Une majuscule ?
9-9
La classe Character
9-10
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
♦ 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
La classe String
Les chaînes de caractères
// caractère d’indice i
int indexOf(char c) // position du caractère c (ou –1)
char charAt(int i)
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-13
9-14
String → char[ ] → String
Les String sont inaltérables
♦ 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
♦ 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 ;
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
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 devient "abcde"
s = new String(data, 1, 3);
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);
}
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-17
Une méthode concat()
9-18
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()
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 */
resTmp = new char[res.length() + tableau[i].length()];
// copie de l’ancien res dans resTmp
for(int i=0; i<res.length(); i+=1)
O( res.length() )
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);
// construit le résultat
O( res.length() + tableau[i].length() )
9-21
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
}
Bilan sur la boucle complète
♦ Si le tampon res est suffisamment grand, il suffit de
copier tous les caractères de tableau[i] à la suite
♦ Avec la concaténation '+'
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
Premier tour : O( 0 + tableau[0].length() ) = c0
Deuxième tour : O (c0 + tableau[1].length() ) = c1
Troisième tour : O (c1 + tableau[1].length() ) = c2
Au total : c0 + c1 + c2 + … + cn
= t0 + (t0+t1) + (t0+t1+t2) + (t0+t1+t2+t3) + …
♦ Avec un tableau de char
Dans le pire des cas : idem
Dans le meilleur des cas, au total:
• t0 + t1 + t2 + t3 + … + tn
• Il suffit que le tampon soit suffisamment grand pour
atteindre le meilleur des cas
9-23
9-24
Démonstration
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
9-25
Algorithme de codage
9-26
Notes personnelles
♦ 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);
}
9-27
9-28
Le décodage
Le codage de chaque caractère
♦ 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
La méthode codeChar()
codeChar(char)
c
[c n’est pas une majuscule]
[c est
une majuscule]
Calculer la position
dans l’alphabet
c est inchangé
Décaler de clé
S’assurer que c est
toujours une lettre
c
9-30
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;
}
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-31
9-32
code() et codeChar()
Résoudre la surcharge
♦ La méthode code : Codeur . String → String
♦ La méthode codeChar : Codeur . char → char
♦ Surcharge :
♦ Comment faire la différence entre code(String) et code(char) ?
♦ Dans BlueJ :
Il suffit de choisir la bonne !
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;
}
9-33
♦ 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
paramètre de type char
9-34
Notes personnelles
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
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
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 :
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'
• 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')
•…
En Java :
"LE RIRE EST LE PROPRE DE L’HOMME"
devient
"KC FURX UMX JK BRHXHY CC Z’HHUCY"
• 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 - décodage
Remarque sur ces codages
♦ 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
A
B
C
D
E
F
G
H
I
J
K
L
M
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
BCDEFGHIJKLMNOPQRSTUVWXYZA
CDEFGHIJKLMNOPQRSTUVWXYZAB
DEFGHIJKLMNOPQRSTUVWXYZABC
EFGHIJKLMNOPQRSTUVWXYZABCD
FGHIJKLMNOPQRSTUVWXYZABCDE
GHIJKLMNOPQRSTUVWXYZABCDEF
HIJKLMNOPQRSTUVWXYZABCDEFG
IJKLMNOPQRSTUVWXYZABCDEFGH
JKLMNOPQRSTUVWXYZABCDEFGHI
KLMNOPQRSTUVWXYZABCDEFGHIJ
LMNOPQRSTUVWXYZABCDEFGHIJK
MNOPQRSTUVWXYZABCDEFGHIJKL
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
9-38
♦ 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
ABCDEFGHIJKLMNOPQRSTUVWXYZ
NOPQRSTUVWXYZABCDEFGHIJKLM
OPQRSTUVWXYZABCDEFGHIJKLMN
PQRSTUVWXYZABCDEFGHIJKLMNO
QRSTUVWXYZABCDEFGHIJKLMNOP
RSTUVWXYZABCDEFGHIJKLMNOPQ
STUVWXYZABCDEFGHIJKLMNOPQR
TUVWXYZABCDEFGHIJKLMNOPQRS
UVWXYZABCDEFGHIJKLMNOPQRST
VWXYZABCDEFGHIJKLMNOPQRSTU
WXYZABCDEFGHIJKLMNOPQRSTUV
XYZABCDEFGHIJKLMNOPQRSTUVW
YZABCDEFGHIJKLMNOPQRSTUVWX
ZABCDEFGHIJKLMNOPQRSTUVWXY
Je code "AAAAAAA" avec la clé privée "ROUGE"
On obtient "ROUGERO"
9-39
9-40

Documents pareils