Cette activité propose de faire le point sur les structures rencontrées jusqu'à maintenant :
Les liens suivants vous permettront de retrouver les informations à connaitre pour manipuler ces données.
Et nous verrons qu'il existe une autre structure bien pratique dans certains cas de figure : les dictionnaires.
Cette partie possède un objectif et un seul : vous faire manipuler les strings.
Vous avez déjà rencontré plusieurs fois les strings : c'est un objet itérable. Il est séquentiel car on peut accéder à son contenu à l'aide d'un index chiffré. Il est non mutable : on ne peut pas le modifier après création. On peut par contre créer un nouveau string qui porte le même nom et qui va écraser l'ancien.
Et surtout : un string ne peut contenir que des caractères (ou plutôt des octets qui représentent des caractères si vous avez suivi les passages sur l'encodage des caractères !).
Nous allons donc travailler sur un fichier texte qui contient un grand nombre de caractères et nous allons tenter de compter les mots et les lettres dans celui-ci.
Commencons par créer un fichier texte contenant uniquement quelques mots et caractères.
01° Créer dans votre espace de travail un dossier qui contiendra le fichier .txt suivant, que vous téléchargerez avec un clic droit + enregistrer la cible de lien sous. Vérifiez (avec Notepad++ par exemple qu'il est bien encodé en UFT-8.
Le fichier est disponible ici : FICHIER TEST UFT8 (avec BOM).
02° Créez, dans le même dossier, un fichier .py contenant le code suivant. Lire ensuite le code ligne par ligne pour tenter de vous souvenir comment il fonctionne.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
objFichier = open("py-024-test-UTF8-avec-BOM.txt","r")
livre = objFichier.read()
objFichier.close()
print(livre)
nA = livre.count('a')
n = len(livre)
pA = 1 / n * nA
print("Le livre contient {} caractères.".format(n))
print("Nombre de a : {0:7}, soit {1:.2%} % des caractères".format(nA,pA))
input("pause")
La correction :
objFichier = open("py-024-test-UTF8-avec-BOM.txt","r")
livre = objFichier.read()
objFichier.close()
On crée un objet-fichier nommé objFichier
à partir du fichier texte nommé "py-024-test-UTF8-avec-BOM.txt"
qu'on ouvre en lecture seule (à l'aide du paramètre fixé à "r"
).
On place dans le string livre
l'intégralité du texte contenu dans l'objet objFichier
à l'aide de la méthode read(). Cette façon de faire à l'avantage d'être simple. Elle a le désavantage de placer la totalité du fichier en mémoire vive. Avec les très gros fichiers, cela peut poser problème. Pour lire le fichier ligne par ligne, voir l'activité sur les fichiers.
On referme l'objet-fichier puisqu'on a réussi normalement à en extraire le contenu.
print(livre)
nA = livre.count('a')
n = len(livre)
pA = 1 / n * nA
J'affiche le contenu pour la forme (avec print(livre)
).
On place le nombre de caractère 'a'
dans la variable nA
.
On place le nombre de caractères au total dans la variable n
avec la fonction native len().
On calcule le pourcentage de a et on place le résultat dans la variable pA
(on a donc pA = 1 si 100% des caractères sont des 'a').
print("Le livre contient {} caractères.".format(n))
print("Nombre de a : {0:7}, soit {1:.2%} % des caractères".format(nA,pA))
input("pause")
On affiche à l'aide d'un print associé à la méthode format() les résultats.
Sur le premier format, on ne place que les accolades { } et le format remplace les accolades avec la variable n.
Sur le second format, on voit qu'on va remplacer les premières accolades par l'élément 0 présent dans le format. Il s'agit de nA. Le :7
indique qu'il faut écrire n en prenant 7 caractères.
La seconde accolade est remplie par le numéro 1 : on va donc chercher l'élément 1 du format. Il s'agit de pA. Le :.2%
indique qu'on doit afficher un pourcentage avec deux chiffres après la virgule (on a noté .2 pas simplement 2). Il va donc multiplier pA par 100 et va arrondir à deux chiffres après la virgule.
03° Lancer le script pour voir s'il fonctionne. Ouvrir ensuite le fichier texte dans Notepad++ par exemple. Cela se passe-t-il comme prévu ?
Sans indiquer d'encodage lors de la lecture du fichier :
Voici un fichier
de test qui contient
quelques caractères.
Le livre contient 66 caractères.
Nombre de a : 2, soit 3.03% % des caractères
pause
Attention : chaque espace compte bien pour un caractère.
Le bilan est assez lourd : non seulement le code du "é" n'est pas décodé correctement depuis fichier en UTF-8 (ici Python a décidé de lire le fichier avec une table Ascii étendu puisqu'il représente les 2 octets du "é" utf-8 en prenant cela pour deux caractères "è") mais en plus il y a des caractères devant le premier caractère "v" ...
A quoi sont dus ces caractères du début ? Au fait que le fichier est encodé en UTF-8 et que dans ce cas, on rajoute un caractère spécial sur 3 octets qui indique qu'il s'agit d'un fichier UTF.
04° Le fichier texte est en UFT-8. Le fichier Python est encodé en UFT-8. Mais Python 3.6 continue de base de décoder les fichiers texte avec une table 8 bits d'ASCII étendu. Indiquer maintenant à Python de décoder votre fichier texte avec la technique de l'UTF-8 à l'aide de l'instruction suivante encoding = 'utf-8'
:
objFichier = open("py-024-test-UTF8-avec-BOM.txt","r", encoding = 'utf-8')
livre = objFichier.read()
objFichier.close()
Voici le résultat :
Voici un fichier
de test qui contient
quelques caractères.
Le livre contient 63 caractères.
Nombre de a : 2, soit 3.17% % des caractères
pause
05° Allez voir la fiche sur les strings et modifiez votre programme pour qu'il transforme tout le texte en minuscules (lowercases en anglais). Demander ensuite au programme de compter les 'a' et les 'e', qu'ils soient minuscules ou majuscules.
Résultat en image :
voici un fichier
de test qui contient
quelques caractères.
Le livre contient 63 caractères.
Nombre de a : 2, soit 3.17% % des caractères
Nombre de e : 7, soit 11.11% % des caractères
pause
J'ai obtenu ce résultat avec le code suivant :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
objFichier = open("py-024-test-UTF8-avec-BOM.txt","r", encoding = 'utf-8')
livre = objFichier.read()
livre = livre.lower() # rajout
objFichier.close()
print(livre)
nA = livre.count('a')
nE = livre.count('e') # rajout
n = len(livre)
pA = 1 / n * nA
pE = 1 / n * nE # rajout
print("Le livre contient {} caractères.".format(n))
print("Nombre de a : {0:7}, soit {1:.2%} % des caractères".format(nA,pA))
print("Nombre de e : {0:7}, soit {1:.2%} % des caractères".format(nE,pE)) # rajout
input("pause")
J'ai donc utiliser la méthode lower() sur le string livre
. Cette méthode renvoie un nouveau string où tous les caractères sont en minuscules. LA METHODE NE TRANSFORME PAS LE STRING : les strings sont non mutables. J'ai donc placé le résultat en minuscules dans un string qui porte le même nom.
D'ailleurs, si livre
est un grand string, il sera plus malin de faire ceci plutôt que de manipuler deux fois le string :
livre = (objFichier.read()).lower()
06° Je suis parti sur un site qui propose des oeuvres tombées dans le domaine public. J'ai téléchargé "The Dunwich Horror" de Lovecraft, enlevé les textes qui ne sont pas dans l'oeuvre. Téléchargez ce fichier sur votre poste et appliquez votre programme à ce long texte.
Le fichier est disponible ici : The Dunwich Horror.
Le site est disponible ici : www.gutenberg.org.
Pourquoi le prendre en anglais alors que les textes de Lovecraft sont dans le domaine public ? Pour des raisons de légalité ! L'oeuvre initiale est dans le domaine public, pas les traductions en français qui sont beaucoup plus récentes.
Le livre contient 101427 caractères.
Nombre de a : 6610, soit 6.52% % des caractères
Nombre de e : 10006, soit 9.87% % des caractères
pause
Par contre, s'il faut faire cela pour tous les caractères, c'est un peu lourd ... Voilà pourquoi nous allons utiliser une autre structure de données : la liste.
La liste est un objet itérable : c'est un ensemble contenant d'autres variables et objets. Comme le string, il est séquentiel : on peut accéder à son contenu avec un index chiffré.
Deux différences majeures : on peut y mettre ce qu'on veut (pas que des caractères comme avec le string) et on peut le modifier après création.
Commençons avec l'une des méthodes des strings : split() qui renvoie une liste contenant l'ensemble des éléments contenus dans un string en prenant comme élément séparateur par défaut "espace"
. Si le string contient une phrase, on peut donc dire que le split() renvoie en quelques sortes la liste des mots. Pour être plus catégorique, il faudrait supprimer les ponctuations ... Mais le but ici n'est pas de réaliser un projet concret mais de voir rapidement l'intérêt des différentes structures de données.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
objFichier = open("py-024-test-UTF8-avec-BOM.txt","r", encoding = 'utf-8')
livre = objFichier.read()
livre = livre.lower() # rajout
objFichier.close()
print(livre)
nA = livre.count('a')
nE = livre.count('e') # rajout
n = len(livre)
pA = 1 / n * nA
pE = 1 / n * nE # rajout
print("Le livre contient {} caractères.".format(n))
print("Nombre de a : {0:7}, soit {1:.2%} % des caractères".format(nA,pA))
print("Nombre de e : {0:7}, soit {1:.2%} % des caractères".format(nE,pE)) # rajout
input("pause")
# nouvelle partie du code
mots = livre.split()
nMots = len(mots)
print(mots)
print("La liste contient ", nMots, " mots")
input("pause")
07° Utiliser le code précédent sur le premier petit fichier. Ne l'utilisez pas sur le second (le livre entier), le print() final de l'ensemble de la liste de mots risquerait d'être un peu trop dur à afficher.
voici un fichier
de test qui contient
quelques caractères.
Le livre contient 63 caractères.
Nombre de a : 2, soit 3.17% % des caractères
Nombre de e : 7, soit 11.11% % des caractères
pause
['\ufeffvoici', 'un', 'fichier', 'de', 'test', 'qui', 'contient', 'quelques', 'caractères.']
La liste contient 9 mots
pause
On a bien 9 mots mais vous pouvez constater que certains "mots" contiennent aussi la ponctuation. C'est donc très perfectible.
On retiendra également qu'une liste est définie comme un ensemble d'éléments séparés par des virgules et délimitée par des crochets [...].
08° Que contient le premier mot ? Rien de choquant ? Sachant que /u
veut dire qu'on donne à la suite les 4 octets (en hexadécimale= du nombre Unicode du caractère, rechercher la signification Unicode du caractère devant v.
...CORRECTION...
Les 4 caractères après \u sont : FEFF.
F vaut 15 en base 10 et E vaut 14.
L'octet de poids faible (à droite) code 1, le suivant 16, le suivant 162(256) et le dernier 163(4096).
Le numéro UNICODE est donc : 15*4096 + 14*256 + 15*16 + 15*1 = 65279
...CORRECTION...
Le caractère UNICODE (FEFF)16 ou 6527910 est trouvable facilement via Internet.
Unicode Character 'ZERO WIDTH NO-BREAK SPACE' (U+FEFF)
Il s'agit du fameux BOM dont j'avais parlé au début.
Invisible à l'affichage, il n'en reste pas moins présent au début du fichier et ses octets l'ont dévoilé !
09° A l'aide de Notepad++, modifier l'encodage des deux fichiers vers UFT-8 sans BOM et recommencer la manipulation sur le premier fichier.
Vous devriez obtenir ceci (à savoir la disparition des caractères \ufeff devant le v) :
['voici', 'un', 'fichier', 'de', 'test', 'qui', 'contient', 'quelques', 'caractères.']
La liste contient 9 mots
pause
Mais nous allons aller plus loin : je voudrais créer un programme qui va automatiquement me créer une liste qui contiendra le nombre d'occurrence des lettres de a à z dans mon string livre
transformé intégralement en minuscules.
Le tout sans taper la recherche de toutes les lettres à la main bien entendu !
Le premier élément de la liste (l'élément 0) devra contenir le nombre de caractère au total.
Le second élément devra contenir le nombre de a.
Le troisième élément devra contenir le nombre de b.
...
Pour cela, vous aller avoir besoin d'utiliser les fonctions chr(i) qui renvoie le caractère UNICODE associé au numéro i. Par exemple, chr(97) donne 'a'
.
Il va falloir créer une liste et la remplir au fur et à mesure avec la méthode append() par exemple. Aller voir la fiche sur les LISTES.
Et bien entendu des boucles et éventuellement des fonctions.
Le programme ne devra plus afficher la liste des mots mais devra afficher la liste des occurrences et réaliser ensuite un affichage plus lisible.
10° A vous de jouer. Réalisez le programme, testez le sur le petit fichier puis sur le livre.
Voici mes résultats sur le fichier basique :
[62, 2, 0, 5, 1, 7, 1, 0, 1, 6, 0, 0, 1, 0, 3, 2, 0, 3, 3, 3, 5, 4, 1, 0, 0, 0, 0] Lettre a présente 2 fois. Lettre b présente 0 fois. Lettre c présente 5 fois. Lettre d présente 1 fois. Lettre e présente 7 fois. Lettre f présente 1 fois. Lettre g présente 0 fois. Lettre h présente 1 fois. Lettre i présente 6 fois. Lettre j présente 0 fois. Lettre k présente 0 fois. Lettre l présente 1 fois. Lettre m présente 0 fois. Lettre n présente 3 fois. Lettre o présente 2 fois. Lettre p présente 0 fois. Lettre q présente 3 fois. Lettre r présente 3 fois. Lettre s présente 3 fois. Lettre t présente 5 fois. Lettre u présente 4 fois. Lettre v présente 1 fois. Lettre w présente 0 fois. Lettre x présente 0 fois. Lettre y présente 0 fois. Lettre z présente 0 fois. pause
Et pour The Dunwich Horror :
[101427, 6610, 1325, 2065, 3662, 10006, 1786, 1758, 5222, 5163, 55, 671, 3647, 1884, 5681, 5750, 1310, 77, 4665, 5026, 7264, 2296, 654, 1922, 88, 1538, 73]
Lettre a présente 6610 fois.
Lettre b présente 1325 fois.
Lettre c présente 2065 fois.
Lettre d présente 3662 fois.
Lettre e présente 10006 fois.
Lettre f présente 1786 fois.
Lettre g présente 1758 fois.
Lettre h présente 5222 fois.
Lettre i présente 5163 fois.
Lettre j présente 55 fois.
Lettre k présente 671 fois.
Lettre l présente 3647 fois.
Lettre m présente 1884 fois.
Lettre n présente 5681 fois.
Lettre o présente 5750 fois.
Lettre p présente 1310 fois.
Lettre q présente 77 fois.
Lettre r présente 4665 fois.
Lettre s présente 5026 fois.
Lettre t présente 7264 fois.
Lettre u présente 2296 fois.
Lettre v présente 654 fois.
Lettre w présente 1922 fois.
Lettre x présente 88 fois.
Lettre y présente 1538 fois.
Lettre z présente 73 fois.
pause
Et voici une proposition de correction :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
objFichier = open("py-024-lovecraft.txt","r", encoding = 'utf-8')
livre = (objFichier.read()).lower()
objFichier.close()
n = len(livre)
occ = []
occ.append(n)
for i in range(97,97+26,1) :
occ.append(livre.count(chr(i)))
print(occ)
for i in range(1,1+26) :
print("Lettre {0} présente {1:6} fois.".format(chr(i+96),occ[i]))
input("pause")
Je crée une liste nommée occ
initialement vide : occ = [].
Je la remplis au fur et à mesure avec la méthode append(). L'élément 0 contient le nombre de caractère total. Puis l'élément 1, le nombre de 'a' ...
Par contre, on voit que la récupération des éléments n'est pas facile. Pour connaitre le nombre de 'd'
, il faut connaître le numéro de l'élément qui doit contenir le nombre correspondant à cette lettre. Pas diffcile sur le moment, mais pas facile ni clair non plus.
Continuons avec la structure suivante : le tuple délimité par les parenthèses (...) et dont les éléments sont séparés par des virgules.
Si on devait résumer cette structure en quelques mots, nous pourrions dire que c'est une liste non mutable.
Comme les listes, les tuples peuvent contenir des éléments divers et variés (c'est une strucure itérable).
Comme les listes, les tuples sont séquentiels (on peut accéder aux éléments via un numéro).
Par contre, ils ne sont pas mutables et c'est une propriété intéressante lorsqu'on veut être certain de ne pas altérer les données.
11° Comparons les listes et tuples à l'aide des tests suivants. Répondre aux questions 2 et 4 et tenter de prévoir l'affichage pour les questions 1 et 3.
>>> x = [1,2,3]
>>> x
[1, 2, 3]
>>> type (x)
*** réponse 1 : donnez la réponse qui devrait s'afficher sur la console ***
>>> x[2] = 10
*** réponse 2 : possible ou non ? ***
>>> y = (1,2,3)
>>> y
(1, 2, 3)
>>> type (y)
*** réponse 3 : donnez la réponse qui devrait s'afficher sur la console ***
>>> y[2] = 10
*** réponse 4 : possible ou non ? ***
...CORRECTION...
1 : x est de type list car on l'a défini avec des crochets.
2 : il est donc possible de modifier directement l'élément 2 et d'obtenir alors liste [1, 2, 10].
3 : y est de type tuple car on l'a défini avec des parenthèses .
4 : on peut obtient donc une erreur car les tuples sont non mutables.
Nous allons utiliser les tuples comme élément à placer dans notre liste occ
précédente : chaque élément de la liste devra être rempli par un simple tuple e
contenant lui même trois élément :
e[0]
contiendra un string contenant uniquement le caractère désiré/li>
e[1]
contiendra le nombre de fois où ce caractère apparait.e[2]
contiendra le pourcentage de présence de ce caractère.Ceci sera valable pour les éléments occ[1]
à occ[26]
. Le premier élément occ[0]
contiendra lui un tuple spécial :
e[0]
contiendra le nom du fichier texte analysé./li>
e[1]
contiendra le nombre de caractères.Rappel : un tuple peut se définir ainsi : e = (element1, element2, element3)
.
12° A vous de jouer. Réalisez ce programme.
Un affichage possible :
[('py-024-lovecraft.txt', 101427), ('a', 6610, 0.0651700237609315), ('b', 1325, 0.013063582675224545), ('c', 2065, 0.02035947035799146), ('d', 3662, 0.036104784722016815), ('e', 10006, 0.09865223264022399), ('f', 1786, 0.017608723515434743), ('g', 1758, 0.017332662900411133), ('h', 5222, 0.05148530470190383), ('i', 5163, 0.050903605548818356), ('j', 55, 0.0005422619223678113), ('k', 671, 0.006615595452887298), ('l', 3647, 0.03595689510682559), ('m', 1884, 0.01857493566801739), ('n', 5681, 0.0560107269267552), ('o', 5750, 0.05669101915663482), ('p', 1310, 0.012915693060033323), ('q', 77, 0.0007591666913149358), ('r', 4665, 0.04599367032446981), ('s', 5026, 0.04955288039673854), ('t', 7264, 0.07161801098326875), ('u', 2296, 0.022636970431936267), ('v', 654, 0.006447987222337247), ('w', 1922, 0.01894958935983515), ('x', 88, 0.0008676190757884981), ('y', 1538, 0.015163615210939887), ('z', 73, 0.0007197294605972768)]
Lettre a présente 6610 fois.
Lettre b présente 1325 fois.
Lettre c présente 2065 fois.
Lettre d présente 3662 fois.
Lettre e présente 10006 fois.
Lettre f présente 1786 fois.
Lettre g présente 1758 fois.
Lettre h présente 5222 fois.
Lettre i présente 5163 fois.
Lettre j présente 55 fois.
Lettre k présente 671 fois.
Lettre l présente 3647 fois.
Lettre m présente 1884 fois.
Lettre n présente 5681 fois.
Lettre o présente 5750 fois.
Lettre p présente 1310 fois.
Lettre q présente 77 fois.
Lettre r présente 4665 fois.
Lettre s présente 5026 fois.
Lettre t présente 7264 fois.
Lettre u présente 2296 fois.
Lettre v présente 654 fois.
Lettre w présente 1922 fois.
Lettre x présente 88 fois.
Lettre y présente 1538 fois.
Lettre z présente 73 fois.
pause
La correction est un peu plus bas, mais tentez de le faire seul. Sinon, on ne rend pas compte de ce qu'on a réellement compris ou non.
Voici le code complet avant les explications :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
fichier = "py-024-lovecraft.txt"
objFichier = open(fichier,"r", encoding = 'utf-8')
livre = (objFichier.read()).lower()
objFichier.close()
n = len(livre)
occ = []
occ.append((fichier,n))
for i in range(97,97+26,1) :
nc = livre.count(chr(i))
e = (chr(i), nc, 1/n*nc)
occ.append(e)
print(occ)
for i in range(1,1+26) :
print("Lettre {0} présente {1:6} fois.".format(occ[i][0],occ[i][1]))
input("pause")
La lecture du livre se fait comme auparavant. La seule différence vient du fait que j'ai placé le nom du fichier dans le string fichier
. De cette façon, la méthode open et la première case de la liste pourront contenir à coup sur le même nom.
Ensuite, on crée toujours une liste vide avec occ = []
.
La différence vient juste après avec la création du premier élément de la liste avec append() qui vient rajouter le tuple suivant : (fichier,n)
. Les parenthèses indiquent bien de générer un tuple dont on donne les deux éléments : le string fichier
et le nombre de caractères n
.
occ = []
occ.append((fichier,n))
Si on avait demandé à voir le contenu de la liste à ce moment, voilà ce qu'on aurait pu afficher (j'ai rajouté les espaces pour que cela soit clair ) :
>>> occ
[ ( 'py-024-lovecraft.txt' , 101427 ) ]
On voit donc que occ
est une liste à l'aide de l'indication 'crochets' : [ ].
On voit que le premier élément (occ[0] donc) est un tuple à l'aide de l'indication 'parenthèses' : ( , ).
Ce tuple contient lui-même deux éléments séparés par une virgule : un string valant 'py-024-lovecraft.txt' et un intéger valant 101427.
C'est ensuite qu'on lance la boucle qui va créer les tuples suivants et les rajouter dans la liste :
for i in range(97,97+26,1) :
nc = livre.count(chr(i))
e = (chr(i)<, nc, 1/n*nc)
occ.append(e)
L'intéger i
commence à 97 et va jusqu'à 97+26 car le boucle continue tant que i < (97+26+1).
On commence par trouver le nombre de caractères chr(i) dans livre
à l'aide du count(). On place le résultat dans nc
.
A la troisième ligne, on crée un tuple (voyez les parenthèses) nommé e
avec le code e = (chr(i) , nc , 1/n*nc )
. Ce tuple contient :
chr(i)
, qui correspond donc à 'a' si i = 97, 'b' si i = 98 ...nc
où ce caractère a été trouvé dans livre
.livre
.Finalement, on rajoute ce tuple à la liste avec occ.append(e)
.
print(occ)
for i in range(1,1+26) :
print("Lettre {0} présente {1:6} fois.".format(occ[i][0],occ[i][1]))
On affiche le contenu de la liste occ
à l'écran pour vérification. Cela permet de vérifier le contenu du premier élément et de vérifier qu'on a bien gérer les caractères jusqu'à 'z".
Vient ensuite la restitution à l'écran. On va lire le contenu à l'aide d'une boucle for numérique. Cela va me permettre de vous expliquer la forme un peu étrange de l'affichage du contenu :
print("Lettre {0} présente {1:6} fois.".format(occ[i][0],occ[i][1]))
Que contient occ[i][0]
? Quelques explications :
occ
est une liste.occ[i]
désigne donc l'élément i de la liste occ
. Cet élément ici est un tuple qu'on pourrait nommer e
.occ[i][0]
revient donc à écrire e[0]
: on va donc voir le contenu de l'élement 0 de l'élement i de la liste occ
.Pour plus de clarté, nous aurions pu taper quelque chose du genre :
print(occ)
for i in range(1,1+26) :
e = occ[i]
print("Lettre {0} présente {1:6} fois.".format(e[0], e[1]))
Nous sommes donc parvenu à créer cette fameuse liste d'occurrence. Mais nous voulions la protéger contre les modifications malheureuses, nous pourrions finaliser l'ensemble avec ceci :
occ = tuple(occ)
Cette simple ligne va créer un tuple nommé occ
à partir de l'ancienne liste. L'avantage ? On ne pourra pas modifier manuellement les éléments. Vous indiquez ainsi que cet ensemble est informatif et ne devrait pas être modifié.
Cela fonctionne dans l'autre sens d'ailleurs si vous voulez transformer un tuple en liste : x = list(x)
transforme le tuple x en liste x.
Mais on pourrait également vouloir créer une liste d'occurrence de tous les caractères présents dans le livre. On peut le faire en manipulant les listes et les strings mais ce n'est pas le cas le plus adapté. Nous allons voir ici une dernière façon de structurer les données : les dictionnaires.
Alors qu'est-ce qu'un dictionnaire ?
Prenons l'exemple d'un dictionnaire au sens usuel du terme, le dico, le livre plein de mots et de définition. Je suis parti sur le site d'un éditeur bien connu de dictionnaire. Et je suis parti voir ce qu'on y disait de l'encodage. Voici la réponse :
Et bien, un dictionnaire au sens Pythonique est assez proche de cela : c'est un objet itérable qui contient d'autres éléments.
Comme un dictionnaire 'physique', on n'accède pas au contenu d'un élément d'un dictionnaire 'Phytonique' à l'aide d'un nombre : si vous cherchez la définition du mot "encodage", vous ne recherchez pas la définition à l'aide du numéro du mot dans le dictionnaire. Vous le recherchez à l'aide du mot-clé "encodage", tout simplement.
Voici deux façons de gérer notre problème de nombres de caractères dans un ouvrage : la première est celle d'une liste remplie de tuples par exemple.
Premier cas : la liste occ
occ = [ 101427, 6610, 1325, 2065 ]
Index numéroté | Contenu de l'élément |
---|---|
0 | 101427 |
1 | 6610 |
2 | 1325 |
3 | 2065 |
Ca fonctionne bien mais il faut savoir que pour obtenir le nombre de 'a'
, il faut chercher occ[1]
. Pas très intuitif.
>>> occ[1]
6610
Deuxième cas : le dictionnaire occ
occ = { 'total' : 101427, 'a' : 6610, 'b' : 1325, 'c' : 2065 }
13° Dans le shell(console) Python, tapez les instructions ci-dessous pour voir comment fonctionne un dictionnaire. Répondre ensuite aux questions :
>>> occ = { 'total' : 101427, 'a' : 6610, 'b' : 1325, 'c' : 2065 }
>>> occ
{'total': 101427, 'a': 6610, 'b': 1325, 'c': 2065}
>>> occ['a']
6610
>>> occ['total']
101427
>>> occ[0]
KeyError: 0
La correction, même si c'est plutôt facile et visuel :
Un dictionnaire est défini à l'aide des accolades { ... }. En effet, les parenthèses définissent les tuples et les crochets définissent les listes.
Contrairement aux listes, tuples et strings ont ne peut pas accéder au contenu d'un dictionnaire en utilisant un index chiffré. Cela provoque même une erreur si aucune clé ne correspond au chiffre utilisé.
Par contre, on peut choisir n'importe quelle clé d'accès.
Comment ? Regardons la strucure d'un dictionnaire :
occ = { 'total' : 101427, 'a' : 6610, 'b' : 1325, 'c' : 2065 }
On remarque que chaque élément est séparé par des virgules comme dans les listes ou les tuples. Rien de nouveau de ce côté.
La nouveauté vient de la présence du double point ':' qui sépare deux parties distinctes de l'élément :
Avant le double point, on trouve la clé permettant d'accéder à ce contenu : il faut taper monDictionnaire[maCle]
pour voir le contenu correspondant à cette clé.
Après le double point, on trouve la valeur qui sera fournie lorsqu'on fait appel à la clé.
Clé | Valeur renvoyé par l'utilisation de la clé |
---|---|
'total' | 101427 |
'a' | 6610 |
'b' | 1325 |
'c' | 2065 |
Ca fonctionne beaucoup mieux : plus besoin de connaitre l'élément contenant ce qu'on cherche pourvu qu'on connaisse la clé de définition !
>>> occ['a']
6610
Vous avez déjà été revoir les fiches sur les strings, les tuples et les listes. Le but de cette fin d'activité n'est pas de vous présenter les dictionnaires en long et en large. Nous allons simplement voir les bases. Et si le besoin s'en fait sentir, vous pourrez toujours aller vous renseigner sur la fiche ou sur Internet.
Nous avons vu comment créer un dictionnaire juste au dessus.
Pas de méthode de type append() ici : il suffit de déclarer la nouvelle clé et sa valeur associée :
>>> occ['d'] = 3662
>>> occ
{'total': 101427, 'a': 6610, 'b': 1325, 'c': 2065, 'd': 3662}
14° Créer quelques clés supplémentaires contenant n'importe quoi : nombres, strings, tuples, listes et même d'autres dictionnaires si vous le voulez. Vous allez voir juste après comment les supprimer donc vous pouvez faire n'importe quoi.
Il faut utiliser la fonction native del() de cette façon :
>>> del occ['b']
>>> occ
{'total': 101427, 'a': 6610, 'c': 2065, 'd': 3662}
>>> del occ['z']
KeyError: 'z'
Comme vous le voyez, c'est facile mais cela provoque également une erreur en cas de clé inexistante.
Nous avons vu que tenter d'accéder à une clé inexistance crée une erreur. C'est facheux ...
Pour vérifier l'existence d'une clé, il suffit d'utiliser un test x in dico
qui teste la présence du mot-clé x
dans le dictionnaire dico
.
>>> 'total' in occ
True
>>> 'a' in occ
True
>>> 'ahah' in occ
False
Avec un code du type if 'a' in occ:
vous avez donc la possibilité de tester la présence d'une clé avant d'en demander la valeur et de provoquer une exception.
Tester une clé, c'est bien mais connaitre la totalité des clés, c'est mieux. Pour cela, il faut utiliser la méthode keys() :
>>> print(occ.keys())
dict_keys(['total', 'c', 'd', 'a'])
>>> for cle in occ.keys():
print(cle, " permet d'accéder à : ", occ[cle])
total permet d'accéder à : 101427
c permet d'accéder à : 2065
d permet d'accéder à : 3662
a permet d'accéder à : 6610
En fait, c'est tellement important que si vous tapez ceci, vous aurez le même effet : transmettre occ
via un in
va calculer occ.keys()
.
>>> for cle in occ:
print(cle, " permet d'accéder à : ", occ[cle])
total permet d'accéder à : 101427
c permet d'accéder à : 2065
d permet d'accéder à : 3662
a permet d'accéder à : 6610
Et voilà, vous savez maintenant comment obtenir l'ensemble des clés et l'utiliser pour lire l'ensemble du contenu.
Il existe deux méthodes similaires à keys() :
La méthode values() renvoie la liste des valeurs stockées et pas les clés.
>>> print(occ.values())
dict_values([101427, 2065, 3662, 6610])
La méthode items() renvoie la liste des tuples (clé, valeur associée).
>>> print(occ.items())
dict_items([('total', 101427), ('c', 2065), ('d', 3662), ('a', 6610)])
Ces trois méthodes ne renvoient pas exactement des listes mais des listes d'un type particulier :
>>> type(occ.keys())
<class 'dict_keys'>
>>> type(occ.values())
<class 'dict_values'>
>>> type(occ.items())
<class 'dict_items'>
N'importe quel type d'entités non-mutables : des nombres, des strings ou des tuples par exemple.
C'est extrémement intéressant pour gérer des cases sur un plateau de jeu par exemple :
toucheCoule = { (10,5):"Bateau", (10,6):"Bateau" }
C'est super pratique pour gérer un plateau sans avoir à stocker plein d'informations sur des cases ... vides.
Il reste encore des choses à dire sur les dictionnaires mais vous aurez voir la fiche une prochaine fois.
Il ne devrait pas vous rester beaucoup de temps et il reste à finaliser ce programme qui permet de comptabiliser automatiquemet les caractères utilisés et leurs nombres.
Question finale :
15° Créer un programme utilisant les dictionnaires qui permet de stocker les caractères utilisés et le nombre de fois où le caractère apparait dans le texte.
Voici un résultat possible, bien améliorable. On remarquera au passage la présence du caractère unicode 65279 : nous avons donc affaire à un fichier qui possédait le caractère BOM.
pause
Le fichier py-024-lovecraft.txt contient 101427 caractères.
Le caractére de code UNICODE 65279 apparaît 1 fois.
Le caractére de code UNICODE 32 apparaît 16231 fois.
Le caractére _ de code UNICODE 95 apparaît 126 fois.
Le caractére t de code UNICODE 116 apparaît 7264 fois.
Le caractére h de code UNICODE 104 apparaît 5222 fois.
Le caractére e de code UNICODE 101 apparaît 10006 fois.
Le caractére d de code UNICODE 100 apparaît 3662 fois.
Le caractére u de code UNICODE 117 apparaît 2296 fois.
Le caractére n de code UNICODE 110 apparaît 5681 fois.
Le caractére w de code UNICODE 119 apparaît 1922 fois.
Le caractére i de code UNICODE 105 apparaît 5163 fois.
Le caractére c de code UNICODE 99 apparaît 2065 fois.
Le caractére o de code UNICODE 111 apparaît 5750 fois.
Le caractére r de code UNICODE 114 apparaît 4665 fois.
Le caractére
de code UNICODE 10 apparaît 1714 fois.
Le caractére b de code UNICODE 98 apparaît 1325 fois.
Le caractére y de code UNICODE 121 apparaît 1538 fois.
Le caractére . de code UNICODE 46 apparaît 856 fois.
Le caractére p de code UNICODE 112 apparaît 1310 fois.
Le caractére l de code UNICODE 108 apparaît 3647 fois.
Le caractére v de code UNICODE 118 apparaît 654 fois.
Le caractére a de code UNICODE 97 apparaît 6610 fois.
Le caractére f de code UNICODE 102 apparaît 1786 fois.
Le caractére " de code UNICODE 34 apparaît 114 fois.
Le caractére g de code UNICODE 103 apparaît 1758 fois.
Le caractére s de code UNICODE 115 apparaît 5026 fois.
Le caractére , de code UNICODE 44 apparaît 987 fois.
Le caractére m de code UNICODE 109 apparaît 1884 fois.
Le caractére - de code UNICODE 45 apparaît 392 fois.
Le caractére æ de code UNICODE 230 apparaît 8 fois.
Le caractére k de code UNICODE 107 apparaît 671 fois.
Le caractére ? de code UNICODE 63 apparaît 15 fois.
Le caractére j de code UNICODE 106 apparaît 55 fois.
Le caractére ! de code UNICODE 33 apparaît 24 fois.
Le caractére x de code UNICODE 120 apparaît 88 fois.
Le caractére : de code UNICODE 58 apparaît 4 fois.
Le caractére 1 de code UNICODE 49 apparaît 31 fois.
Le caractére ' de code UNICODE 39 apparaît 533 fois.
Le caractére q de code UNICODE 113 apparaît 77 fois.
Le caractére ; de code UNICODE 59 apparaît 104 fois.
Le caractére z de code UNICODE 122 apparaît 73 fois.
Le caractére 9 de code UNICODE 57 apparaît 17 fois.
Le caractére 2 de code UNICODE 50 apparaît 15 fois.
Le caractére 8 de code UNICODE 56 apparaît 6 fois.
Le caractére 6 de code UNICODE 54 apparaît 6 fois.
Le caractére 7 de code UNICODE 55 apparaît 9 fois.
Le caractére 4 de code UNICODE 52 apparaît 4 fois.
Le caractére 0 de code UNICODE 48 apparaît 4 fois.
Le caractére 5 de code UNICODE 53 apparaît 7 fois.
Le caractére 3 de code UNICODE 51 apparaît 5 fois.
Le caractére ( de code UNICODE 40 apparaît 1 fois.
Le caractére ) de code UNICODE 41 apparaît 1 fois.
Le caractére [ de code UNICODE 91 apparaît 3 fois.
Le caractére ] de code UNICODE 93 apparaît 3 fois.
Le caractére ä de code UNICODE 228 apparaît 1 fois.
Le caractére * de code UNICODE 42 apparaît 5 fois.
Le caractére é de code UNICODE 233 apparaît 1 fois.
Le caractére ü de code UNICODE 252 apparaît 1 fois.
pause
Une possibilité de correction :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
fichier = "py-024-lovecraft.txt"
objFichier = open(fichier,"r", encoding = 'utf-8')
livre = (objFichier.read()).lower()
objFichier.close()
n = len(livre)
occ = {}
occ['total'] = (fichier,n)
for c in livre :
if c not in occ :
occ[c] = livre.count(c)
input("pause")
print("Le fichier ",fichier, " contient ",n, " caractères.")
for cle in occ :
if not cle == 'total' :
texte = 'Le caractére {0} de code UNICODE {2} apparaît {1:6} fois.'
print(texte.format(cle,occ[cle],ord(cle)))
input("pause")