TP 5 : Chaînes de caractères

Transcription

TP 5 : Chaînes de caractères
MPSI 831
Lycée Masséna
TP 5 : Chaînes de caractères
1
Rappels sur les chaînes de caractères.
Rappels sur la manipulation. On a déja vu les chaînes de caractères, en les utilisant avec la fonction d’affichage
print. Rappelons que :
• on crée un objet de type « chaîne de caractères » (str) en écrivant un texte entre apostrophes ('une chaine') ;
guillemets ("une autre chaîne") ou encore entre triple guillemets, cette dernière méthode permettant de créer
des chaînes s’étendant sur plusieurs lignes.
• l’accès à des caractères se fait comme pour les listes ou les tuples : si s est une chaîne de caractères, s[0] est
son premier caractère, s[len(s)-1] son dernier. On peut aussi utiliser des indices négatifs (compris entre −n
et −1, avec n la taille de la chaîne), par exemple s[-1] est similaire à s[len(s)-1. L’accès avec tout autre
indice provoque une erreur.
• le slicing fonctionne de la même manière que pour les listes ou les tuples : s[i:j] est la sous-chaîne contenant
les caractères s[i],...,s[j-1]. Ce mécanisme est tolérant avec les indices trop grands et trop petits, et
on peut utiliser des indices négatifs ou encore un pas : s[0:len(s):2] (équivalent à s[::2]) est la chaîne
constituée d’un caractère sur 2 de s à partir du premier, s[len(s)-1:-1:-1] (équivalent à s[::-1]) est la
chaîne constituée des caractères de s à l’envers.
• les chaînes de caractères sont très semblables à des tuples de caractères : elles sont immuables (on ne peut
modifier un caractères).
• Comme pour les listes ou les tuples, concaténer deux chaînes de caractères se fait avec +. L’équivalent du tuple
vide () ou de la liste vide [] est la chaîne vide "".
Une chaîne de caractères : un itérable. Comme beaucoup de structures complexes en Python, les chaînes de
caractères sont des itérables : on peut parcourir directement les caractères à l’aide d’une boucle for, essayez ceci :
for caractere in "une chaîne de caractères":
print(caractere)
D’une chaîne à une liste et réciproquement. Comme souvent avec les itérables, il est possible « d’éclater »
une chaîne en une liste, essayez list("chaine"). Inversement, il est possible de réunir les caractères d’une liste de
caractères en un mot, essayez "".join(['m', 'o', 't']).
On a utilisé ici la méthode join sur la chaîne de caractères vide. Ceci peut-être appliqué avec une chaîne quelconque
(essayez par exemple "-".join(['m', 'o', 't'])). Les éléments de la chaîne peuvent être des mots plus complexes
que de simples caractères, essayez !
Enfin, on peut séparer des mots d’une chaîne autour d’un motif particulier, à l’aide de la méthode split. Essayez par
exemple "Ceci est une phrase".split(" ").
La table des caractères ASCII. Les 128 caractères de la table ASCII comprennent les lettres minuscules et
majuscules non accentuées, les chiffres de 0 à 9, à peu près tous les caractères d’un clavier QWERTY (pas de lettres
accentuées !), et des caractères d’espacement 1 . À chaque caractère est accocié un code entre 0 et 127 (donc représentable
sur 7 bits). En Python, les fonctions ord et chr permettent de passer d’un caractère à son numéro et réciproquement,
essayez les boucles suivantes :
for i in range(97,97+26):
print(chr(i))
for caractere in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
print(ord(caractere))
for i in range(10): #conversions des chiffres en lettres.
print(ord(str(i))):
On notera que les lettres minuscules (respectivement les lettres majuscules, les chiffres) sont codés par des numéros
contigus, avec 'a' (respectivement 'A', '0') ayant le plus petit numéro.
1. La table complète se trouve par exemple ici : http://www.table-ascii.com/.
Svartz
Page 1/5
2015/2016
MPSI 831
2
Lycée Masséna
Manipulations élémentaires de chaînes
Exercice 1. Écrire une fonction qui affiche à l’écran les préfixes d’un mot (le mot vide est un préfixe), en utilisant le
slicing.
>>> prefixes('Python')
P
Py
Pyt
Pyth
Pytho
Python
Exercice 2. Faire de même avec les suffixes.
>>> suffixes('abc')
abc
bc
c
>>>
Exercice 3. Écrire une fonction miroir, qui renvoie une chaîne de caractères étant l’inverse de celle passée en argument.
>>> miroir('Python')
'nohtyP'
Exercice 4. Écrire une fonction egales qui teste l’égalité de deux chaînes de caractères, en utilisant le test d’égalité
(==) ou de différence (!=) uniquement sur des caractères et pas sur la chaîne complète (bien que cela fonctionne !). La
fonction doit renvoyer un booléen.
>>> egales('python','pythoN')
False
Exercice 5. Déduire des deux fonctions précédente une fonction palindrome, qui teste si le mot passé en entrée est
un palindrome (un mot égal à son miroir). La fonction renvoie un booléen.
>>> palindrome('ressasser')
True
3
Un peu de cryptographie : cryptanalyse du chiffrement de César
La cryptanalyse est la science du décodage des messages codés. Le principe est simple, le texte d’origine est
transformé (on parle de chiffrement) à l’aide d’une clé, qui est secrète. Quiconque possède la clé de chiffrement est
capable de déchiffrer le message. Le travail du cryptanalyste consiste à casser le chiffrement, c’est à dire découvrir la
clé de chiffrement pour retrouver le message d’origine.
3.1
Texte en minuscules et liste de nombres associée
On cherche à chiffrer un texte composé de caractères en minuscules (soit 26 lettres différentes) représentés par des
entiers compris entre 0 et 25, avec l’identification naturelle : 0 ↔ a, 1 ↔ b, ..., 25 ↔ z.
Ainsi, le texte lyceemassena est représenté comme la troisième ligne du tableau ci-dessous (la première ligne
représente les indices des lettres).
0
1 2
l
y c
11 24 2
3
e
4
4
e
4
5 6
m a
12 0
7
s
18
8 9
s e
18 4
10 11
n a
13 0
En Python, on utilisera simplement la liste [11, 24, 2, 4, 4, 12, 0, 18, 18, 4, 13, 0] pour représenter le
texte lyceemassena.
Svartz
Page 2/5
2015/2016
MPSI 831
Lycée Masséna
Exercice 6. Transformation d’un texte en minuscules en chiffres. À l’aide de la fonction ord, écrire une fonction
en_chiffres(t) prenant en entrée un texte composé uniquement de lettre en minuscules et retournant la liste associée,
composée de chiffres entre 0 et 25.
Exercice 7. Fonction réciproque. Écrire la fonction en_lettres(L) prenant en entrée une liste constituée de chiffres
de 0 à 25 et renvoyant le texte en minucules associé. On utilisera la fonction chr et la méthode join.
Exercice 8. Écrire une fonction en_minuscules(t) prenant en entrée une chaîne de caractères et en retournant une
autre, où :
— tous les caractères qui ne sont pas des lettres ASCII (de a à z et de A à Z) ont été supprimés ;
— toutes les lettres majuscules ont été remplacées par des lettres minuscules.
Vous pourrez ainsi travailler avec de longs textes piochés par exemple sur Wikipédia comme exemple pour le TP.
Dans la suite, un texte à chiffrer ou à déchiffrer sera toujours une telle liste de nombres.
3.2
Chiffrement de César
Le chiffrement est un des plus rudimentaires. Il a été utilisé par Jules César pour certaines de ses correspondances.
Le principe est de décaler les lettres de l’alphabet d’une ou plusieurs positions, la clé de chiffrement étant la valeur
du décalage. Par exemple, en décalant les lettres d’une position, le caractère a se transforme en b, le b en c,..., le z en
a. Inversement, décaler les lettres de 25 transforme le a en z, etc... Avec un décalage de 25 le texte avecesar devient
donc zudbdrzq. Remarquez qu’un décalage de 26 ou d’un de ces multiples laisse le texte inchangé.
Exercice 9. Écrire la fonction chiffrement_cesar(L,d) qui prend en arguments une liste L et un entier d, et qui
renvoie une nouvelle liste de même taille que L contenant les chiffres de L décalés de d positions. On utilisera le modulo.
>>> chiffrement_cesar([0, 21, 4, 2, 4, 18, 0, 17], 25) #correspond à avecesar, qu'on décale de 25.
[25, 20, 3, 1, 3, 17, 25, 16]
Exercice 10. Écrire de même la fonction dechiffrement_cesar(L,d) prenant les mêmes arguments mais qui réalise
le décalage dans l’autre sens.
>>> dechiffrement_cesar([25, 20, 3, 1, 3, 17, 25, 16], 25) #déchiffrement de zudbdrzq
[0, 21, 4, 2, 4, 18, 0, 17]
3.3
Cryptanalyse du chiffrement de César
Pour réaliser la cryptanalyse, il faut découvrir la valeur du décalage, qu’on va essayer de deviner automatiquement.
L’approche la plus couramment employée est de regarder la fréquence d’apparition de chaque lettre dans le texte
chiffré. En effet, la lettre la plus fréquente dans un texte suffisamment long en français est la lettre e.
Exercice 11. Écrire la fonction frequences(L) qui prend en argument une liste représentant un texte composé
uniquement de minuscules, et qui retourne une liste de taille 26 dont la case d’indice i (avec 0 ≤ i < 26) contient le
nombre d’apparitions du nombre i dans L.
>>> frequences([1, 14, 18, 20, 20, 2, 16, 8, 8, 20, 3, 16])
[0, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 0, 0, 0, 0]
Exercice 12. Écrire une fonction indice_max(T) prenant en entrée une liste d’entiers (quelconques) T et retournant
l’indice du maximum de la liste. On suppose celui-ci unique.
>>> indice_max([0,7,8,10,1]) #10, d'indice 3, et le maximum.
3
Exercice 13. Écrire la fonction dechiffrement_auto(L) qui prend en argument une liste L représentant un texte
chiffré, et qui renvoie la liste correspondant au texte d’origine (en calculant la clé pour que la lettre e soit la plus
fréquente dans le texte déchiffré, en supposant qu’il n’y a qu’une possibilité).
>>> dechiffrement_auto([1, 14, 18, 20, 20, 2, 16, 8, 8, 20, 3, 16])
[11, 24, 2, 4, 4, 12, 0, 18, 18, 4, 13, 0]
Dans l’exemple précédent, on retrouve bien la liste correspondant au texte lyceemassena, ce qui n’est pas étonnant :
la lettre e est celle qui apparaît le plus. Le texte initial avait été décalé de 16.
Svartz
Page 3/5
2015/2016
MPSI 831
4
Lycée Masséna
Chiffrement de Vigenère
La plupart d’entre vous avait déja fait la section d’avant puisque c’était le DS 2 des PCSI. Voici de quoi faire !
Au XVIème siècle, Blaise de Vigenère a modernisé le chiffrement de César très peu résistant de la manière suivante.
Au lieu de décaler toutes les lettres du texte de la même manière, on utilise un texte clé (de petite taille) qui donne
une suite de décalages.
Prenons par exemple la clé mpsi. Pour chiffrer un texte, on code la première lettre en utilisant le décalage qui
envoie le a sur le m (la première lettre de la clé). Pour la deuxième lettre, on prend le décalage qui envoie le a sur le p,
de même pour la troisième et la quatrième lettre avec s et i. Puis, pour la cinquième, on reprend la clé à partir de sa
première lettre. Sur l’exemple lyceemassena avec la clé mpsi, on obtient le tableau qui suit. La première ligne donne
le texte, la seconde la lettre de la clé utilisée pour le décalage et la troisième le texte chiffré. Ici le nombre de lettres
de la clé divise la taille du texte, mais c’est fortuit.
l
m
x
4.1
y
p
n
c
s
u
e
i
m
e
m
q
m
p
b
a
s
s
s
i
a
s
m
e
e
p
t
n
s
f
a
i
i
Chiffrement par la méthode de Vigenère
Exercice 14. Écrire la fonction chiffrement_vigenere(L,c) qui prend comme arguments une liste d’entiers L représentant le texte à chiffrer, et une liste d’entiers c donnant la clé servant au chiffrement, et qui retourne une liste de
même taille que L contenant les entiers correspondant au texte chiffré.
>>> #chiffrement de lyceemassena par la cle mpsi
>>> chiffrement_vigenere([11, 24, 2, 4, 4, 12, 0, 18, 18, 4, 13, 0],[12,15,18,8])
[23, 13, 20, 12, 16, 1, 18, 0, 4, 19, 5, 8]
4.2
Cryptanalyse du chiffrement
Maintenant, on suppose disposer d’un texte t assez long (xnumqbsaetfi n’est pas un exemple pertinent !) chiffré
par la méthode de Vigenère, et on veut retrouver le texte d’origine t0. Pour cela, on doit trouver la clé c ayant servi
au chiffrement. On procède en deux temps :
— détermination de la longueur k de la clé c,
— détermination des lettres composant c.
La première étape est la plus difficile. On remarque que deux lettres identiques dans t0 espacées de p × k caractères
(où p est un entier et k la taille de la clé) sont codées par la même lettre dans t. Mais cette condition n’est pas
suffisante pour déterminer la longueur k de la clé c puisque des répétitions peuvent apparaître dans t sans qu’elles
existent dans t0.
Pour éviter ce problème, on recherche les répétitions non pas d’une lettre mais de séquences de ` lettres dans t
puisque deux séquences de lettres répétées dans t0, dont les premières lettres sont espacées par p × k caractères, sont
aussi chiffrées par deux mêmes séquences dans t.
Dans la suite, on ne considère que des séquences de taille 5 en supposant que toute répétition d’une séquence de
5 lettres dans t provient exclusivement d’une séquence de 5 lettres répétée dans t0. Ainsi, la distance séparant ces
répétitions donne des multiples de k. La valeur de k est obtenue en prenant le PGCD (plus grand diviseur commun)
de tous ces multiples. Si le nombre de répétitions est suffisant, on a de bonnes chances d’obtenir la valeur de k. On
suppose donc que cette assertion est vraie. La fonction suivante, que l’on pourra utiliser librement, donne le PGCD
de deux entiers positifs a et b (avec P GCD(a, b) = P GCD(b, a) = a si b = 0).
def PGCD(a,b):
while b!=0:
a,b=b,a%b
return a
On définit le PGCD de plusieurs entiers comme le plus grand diviseur commun de tous ces entiers. Il peut être
calculé en appliquant plusieurs fois la fonction PGCD ci-dessus.
Exercice 15. ** Écrire la fonction pgcd_distances_repetitions(L,i) qui prend en argument le texte chiffré L et
un entier i (0 ≤ i ≤ n − 5) qui est l’indice d’une lettre dans L ; et qui retourne le pgcd de toutes les distances entre
Svartz
Page 4/5
2015/2016
MPSI 831
Lycée Masséna
les répétitions de la séquence de 5 lettres L[i:i+5] dans la suite du texte L[i+5], L[i+6],..., L[n-1]. Cette fonction
retourne 0 s’il n’y a pas de répétition. Pour tester l’égalité de deux séquences de longueur 5 démarrant aux indices j
et k, on pourra utiliser L[j:j+5]==L[k:k+5].
Exercice 16. Écrire la fonction longueur_cle(L) qui prend en argument le texte chiffré L, et qui retourne la longueur
k de la clé de chiffrement.
Exercice 17. Une fois la longueur de la clé connue, écrire la fonction dechiffrement_vigenere(L,k) prenant en
entrée le texte à déchiffrer et la longueur de la clé, et retournant le texte d’origine.
Indication : il suffit de faire une variante de l’algorithme proposé pour déchiffrer un texte chiffré par la méthode
de César, mais il y a un décalage différent pour chaque lettre de la clé.
Exercice 18. Enfin, écrire la fonction dechiffrement_auto_vigenere(L) permettant de déchiffrer de manière automatique un texte chiffré par la méthode de Vigenère. Essayez avec quelques textes longs récupérés sur internet.
Les fonctions de toute la partie cryptographie sont à terminer pendant les vacances. Envoyez
à l’adresse [email protected] un fichier Python contenant uniquement les fonctions (je
ne veux pas y voir autre chose que des fonctions !). Assurez vous que les noms de l’énoncé ont
été scrupuleusement respectés. Sur la première ligne du fichier (avant les triple quotes
ajoutées automatiquement par Spyder...), placez votre adresse mail précédée du caractère #.
Svartz
Page 5/5
2015/2016