Infoforall

Python 20 : LOGIQUE ET GESTION DES FICHIERS TEXTE

Nous avons vu dans les parties précédentes comment gérer les chaînes de caractères. Retenons dans un premier temps que chaque caractère est codé par un code.

Nous allons commencer par voir comment lire et enregistrer des textes dans un fichier texte.

Après des explications sur la gestion des répertoires, nous verrons comment créer des programmes qui parviennent à modifier ou gérer un ensemble de fichiers contenus dans un répertoire plutôt qu'un fichier à la fois comme dans nos anciennes activités.

Et pour finir, nous utiliserons nos nouvelles connaissances pour améliorer le jeu du pendu : cette fois, nous pourrons aller choisir un mot au hasard dans un ensemble de mots enregistrés dans un fichier.

1 - Enregistrer des textes dans un fichier-texte

Nous allons commencer par créer un objet-fichier (nommé obj_fichier) et le lier à un fichier-texte (nommé mon_fichier.txt) que nous allons créer sur le disque dur.

Pour créer le fichier-texte et le lier à l'objet, c'est un peu bizarre : il faut utiliser une sorte de fonction-constructeur nommée open et pas new.

Voilà le code à utiliser :

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","w")

obj_fichier.close()

Les deux premières lignes ne sont pas obligatoires mais nous allons commencer à les placer systématiquement car il s'agit de bonnes pratiques : on indique avec la première que le code est en Python (non indispensable ici puisqu'on lit précisement un fichier .py) et la deuxième ligne que les caractères du fichier Python sont encodés en utf-8(non indispensable car il s'agit de l'encodage par défaut de Python : si rien n'est précisé, l'interpréteur Python tenterait de lire le fichier .py en utilisant l'utf-8).

La quatrième ligne obj_fichier = open("mon_fichier.txt","w") crée l'objet-fichier ET le fichier associé.

La cinquième ligne obj_fichier.close() referme correctement le fichier.

01° Tester le code en créant un fichier Python test.py. Vérifier que le fichier txt est bien créé dans le même dossier que votre fichier Python.

Attention : si vous passez par IDLE Python, vous n'avez pas à préciser l'encodage : IDLE encode automatiquement votre texte en utf-8. Par contre, avec Notepad++, pensez bien à préciser l'encodage avant de taper votre code. Nous verrons pourquoi plus loin.

Normalement, avant lancement du code Python, vous aviez cela :

votre dossier de travail

test.py

Après l'avoir lancé, vous avez cela :

votre dossier de travail

test.py

mon_fichier.txt

02° Ouvrez le fichier texte : vous devriez voir qu'il est vide. Nous l'avons juste créé.

Bien, il nous reste à remplir notre fichier avec un peu de texte.

Pour écrire dans un fichier, nous allons utiliser une méthode qui se nomme write.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","w")

obj_fichier.write("-- Ouverture --")

obj_fichier.write("Première entrée.")

obj_fichier.write("Deuxième entrée.")

obj_fichier.write("Troisième entrée.")

obj_fichier.write("-- Fermeture --")

obj_fichier.close()

On dit que l'écriture est séquentielle.

03° Lancer ce nouveau code remplaçant le précédent. Que contient le fichier-texte après l'exécution du code ?

Et oui. On ne passe pas automatiquement à la ligne. Vous devriez avoir un contenu ressemblant à 

-- Ouverture --Première entrée.Deuxième entrée.Troisième entrée.-- Fermeture --

Pour cela, ce n'est pas très difficile : le passage à la ligne peut s'imposer avec \n en Python. Nous allons donc modifier le code en rajoutant les passages à la ligne et les lignes devraient venir s'ajouter à celle déjà dans le fichier :

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","w")

obj_fichier.write("-- Ouverture --\n")

obj_fichier.write("Première entrée.\n")

obj_fichier.write("Deuxième entrée.\n")

obj_fichier.write("Troisième entrée.\n")

obj_fichier.write("-- Fermeture --\n")

obj_fichier.close()

On s'attend à avoir un fichier-texte contenant :

-- Ouverture --Première entrée.Deuxième entrée.Troisième entrée.-- Fermeture --

-- Ouverture --

Première entrée.

Deuxième entrée.

Troisième entrée.

-- Fermeture --

04° Lancer ce nouveau code en appliquant la modification. Le contenu du fichier-texte est-il conforme au résultat attendu ?

Et oui, votre programme n'a pas rajouté de contenu au fichier précédent : il l'a écrasé pour placer le nouveau contenu... Comment faire pour que le fichier ne soit pas écrasé lorsqu'on rajoute du contenu ?

C'est facile : il faut modifier l'un des paramètres de la fonction open. En effet :

Comme notre fichier existe déjà, nous allons venir lui rajouter des données en utilisant un open par append :

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","a")

obj_fichier.write("-- Ouverture en rajout --\n")

obj_fichier.write("4e entrée.\n")

obj_fichier.write("5e entrée.\n")

obj_fichier.write("6e entrée.\n")

obj_fichier.write("-- Fermeture --\n")

obj_fichier.close()

05° Modifier le fichier python, relancer. Vous devriez obtenir un fichier texte contenant

-- Ouverture --

Première entrée.

Deuxième entrée.

Troisième entrée.

-- Fermeture --

-- Ouverture en rajout --

4e entrée.

5e entrée.

6e entrée.

-- Fermeture --

2 - Lire un fichier-texte avec read

Voilà les différentes méthodes pour lire le contenu d'un fichier-texte lié à l'objet-fichier nommé obj_fichier. On notera tout d'abord qui nous allons créer l'objet-fichier en utilisant la fonction open avec l'argument "r" comme read (lire en anglais).

Méthode read()

Première lecture : la lecture entière directe :

Utilisation de la méthode read : print(obj_fichier.read())

05° Lancer le code suivant sur votre fichier texte.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","r")

print(obj_fichier.read())

obj_fichier.close()

input("pause")

Avec le fichier précédent, on devrait avoir:

-- Ouverture --

Première entrée.

Deuxième entrée.

Troisième entrée.

-- Fermeture --

-- Ouverture en rajout --

4e entrée.

5e entrée.

6e entrée.

-- Fermeture --

06° Ouvrir le fichier texte dans Notepad++. Activer afficher - symboles spéciaux - tous

Vous devriez voir que le fichier txt contient d'autres caractères que les caractères visibles : il contient également les sauts à la ligne. C'est d'ailleurs grace à ces caractères que Python parvient à distinguer les lignes.

En bref, la méthode read() permet de lire l'intégralité du fichier, caractères non imprimables compris.

On notera qu'on peut aussi dire de ne lire qu'un nombre limité de caractères : il suffit de rajouter le nombre de caractères en argument de la méthode : read(10) ne va vous renvoyer que les 10 caractères suivants.

Méthode readlines()

Mais si on veut lire les lignes enregistrées une à une ? Et bien, il faut une deuxième méthode de lecture : via l'utilisation de la méthode readlines() qui place les différentes lignes dans une liste dont chaque case contient l'une des lignes.

On obtient l'instruction print(obj_fichier.readlines())

07° Modifier le code précédent avec cette façon de faire.

Avec le fichier précédent, on devrait avoir:

['-- Ouverture --\n' ,'Première entrée.\n' ,'Deuxième entrée.\n' ,'Troisième entrée.\n' ,'-- Fermeture --\n' ,'-- Ouverture en rajout --\n' ,'4e entrée.\n' ,'5e entrée.\n' ,'6e entrée.\n' ,'-- Fermeture --\n']

Nous n'avons pas vraiment une lecture ligne par ligne à l'écran. Mais les lignes sont stockées dans une sorte de tableau, qu'on nomme une liste.

Bon, du coup, nous allons avoir besoin d'en apprendre plus sur les listes...

Première rencontre avec les listes ?

Qu'est qu'une liste ? C'est une suite ordonnée et modifiable (mutable) d'objets ou valeurs quelconques.

On peut donc dire que le string est sorte de liste en fait (mais le string n'est pas modifiable après création, contrairement aux listes). Une liste qui ne contient que des variables de type char, et qui n'est pas modifiable elle. Mais une sorte de liste néanmoins. Vous allez voir qu'on retrouve beaucoup de choses vues avec les strings d'ailleurs.

Les éléments d'une liste sont séparés par des virgules et la définition de la liste commence avec [ et s'arrête avec ].

ma_liste = [1,'a',45.2,"bonjour",'b'] crée une liste de 5 éléments contenant l'integer 1, le char a, le float 45.2, le string bonjour et le char b.

Si on veut afficher une liste, print(ma_liste) fonctionne.

[1,'a',45.2,'bonjour','b"]

Si on veut connaitre le nombre d'éléments dans une liste, len(ma_liste) fonctionne.

5

Si on veut accéder à l'un des éléments d'une liste, on tapera ma_liste[2], mais attention le premier élément est l'élément 0.

Si on demande print(ma_liste[2]) avec ma_liste = [1,'a',45.2,"bonjour",'b'], on obtient :

45.2

Si on veut accéder à un ensemble d'éléments d'une liste, on tapera ma_liste[1:4], et on aura les éléments 1,2 et 3 (car on a une commande du type i<4).

Si on demande print(ma_liste[1:4]) avec ma_liste = [1,'a',45.2,"bonjour",'b'], on obtient :

['a',45.2,'bonjour']

Pour parcourir une liste, il suffit d'utiliser un for.

for i in ma_liste :

    print(i)

Et on obtient alors

1

a

45.2

bonjour

b

Pourquoi parler des listes ? Vous vous souvenez ? Contrairement à la méthode read, la méthode readlines() renvoie une liste des différentes lignes :

On peut donc lire les lignes d'un fichier txt à l'aide d'un for correctement écrit. Voici un exemple :

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","r")

lecture = obj_fichier.readlines()

print("--- 1 Résultat de la lecture de la liste avec readlines ---")

print(lecture)

print("\n\n\n")

print("--- 2 Recherche ligne par ligne dans la liste avec un for ---")

for ligne in lecture :

    print(ligne,end='')

obj_fichier.close()

input("pause")

08° Utiliser ce code pour vérifier qu'on obtient bien une lecture ligne par ligne.

--- 1 Résultat de la lecture de la liste avec readlines ---

['-- Ouverture --\n', 'Première entrée.\n', 'Deuxième entrée.\n', 'Troisième entrée.\n', '-- Fermeture --\n', '-- Ouverture en rajout --\n', '4e entrée.\n', '5e entrée.\n', '6e entrée.\n', '-- Fermeture --\n']



--- 2 Recherche ligne par ligne dans la liste avec un for ---

-- Ouverture --

Première entrée.

Deuxième entrée.

Troisième entrée.

-- Fermeture --

-- Ouverture en rajout --

4e entrée.

5e entrée.

6e entrée.

-- Fermeture --

Python permet une lecture assez intuitive des fichiers texte non ?

Au fait, pourquoi mettre un end='' dans le code du print ? C'est simple : la première ligne contient "-- Ouverture --\n" et intègre donc le changement de ligne. Or, le print "normal" provoque un saut de ligne. J'ai donc utilisé un print remplaçant le saut de ligne usuel par '' (rien). Ainsi, on saute à la ligne uniquement une fois, par le saut de ligne intégré dans la chaîne de caracctère.

On retiendra que pour parcourir toutes les lignes après les avoir stockées dans la liste lecture, il faut utiliser ce code :

obj_fichier = open("mon_fichier.txt","r")

lecture = obj_fichier.readlines()

for ligne in lecture :

    print(ligne, end='')

obj_fichier.close()

Avancement dans le fichier : Une fois le fichier ouvert et lu, vous êtes en fin de fichier (end of file). C'est exactement comme quand vous lisez un livre page par page. A la fin du livre, vous ne pouvez pas continuer de lire à moins de repartir au début. Vous, vous savez le faire sans qu'on vous en donne l'ordre. Pas l'ordinateur...

Si vous voulez repartir au début du fichier, il faut utiliser le code suivant obj_fichier.seek(0) et l'ordinateur partira à nouveau au début du fichier.

Méthode readline()

Il reste une méthode permettant de lire un fichier : la méthode readline, sans s.

Contrairement à readlines avec un s, qui lit toutes les lignes d'un coup, readline ne va vous donner qu'une ligne à la fois. Laquelle ? Celle devant laquelle l'ordinateur pointe.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","r")

print(obj_fichier.readline())

obj_fichier.close()

input("pause")

Ce code vous donnerait ceci avec le fichier précédent

-- Ouverture --

Si on rajoute un second readline :

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","r")

print(obj_fichier.readline())

print(obj_fichier.readline())

obj_fichier.close()

input("pause")

Ce code vous donnerait ceci avec le fichier précédent : (comme vous le voyez, nous aurions du rajouter un end='' dans le print)

-- Ouverture --

Première entrée.

Après l'ouverture d'un fichier ou un seek(0), il vous donnera la première ligne. Si vous reutilisez un readline, il aura avancé d'un cran et vous donnera donc la deuxième ligne.

Un exemple avec le fichier précédent ? Imaginons qu'on lance deux readline, qu'on fasse un seek(0) et qu'on refasse deux readline. Quelles vont être les lignes lues par l'ordinateur ?

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os

obj_fichier = open("mon_fichier.txt","r")

print(obj_fichier.readline())

print(obj_fichier.readline())

obj_fichier.seek(0)

print(obj_fichier.readline())

print(obj_fichier.readline())

obj_fichier.close()

os.system("pause")

09° Demandez vous ce qui devrait s'afficher puis utilisez le code.

L'ordinateur commence par lire les deux premières lignes, va revenir au début et va relire les deux premières lignes.

-- Ouverture --

Première entrée.

-- Ouverture --

Première entrée.

Encore une fois, on obtient une lecture séquentielle : la valeur d'une lecture dépend de la position précédente du pointeur de lecture.

Pour lire l'intégralité d'un fichier avec un readline, nous pouvons utiliser une boucle while reglée pour ne pas en sortir ( while 1: ou while True: feront l'affaire), et tenter de lire la ligne. Si la ligne n'existe pas, c'est qu'on est arrivé à la fin du fichier. On sort alors à l'aide d'un break. Sinon, on affiche la ligne à l'écran par exemple.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("mon_fichier.txt","r")

while 1 : # La condition est donc toujours True

    ligne = obj_fichier.readline() # On tente de lire la ligne

    if not(ligne) : # Si ligne n'existe pas, elle contient False

        break # On sort de la boucle

    else : # Sinon, c'est que ligne contient quelque chose

        print(ligne)

obj_fichier.close()

input("pause")

10° Tester cette méthode de lecture.

Vous le voyez, c'est un peu plus délicat d'utiliser un readline. L'intérêt par contre est qu'on ne stocke pas toutes les lignes en mémoire contrairement à read ou readlines.

3 - Gestion des adresses

Ouvrir un fichier, c'est bien mais savoir le choisir, c'est mieux.

Nous allons commencer par expliquer la notion de chemin relatif pour trouver un fichier ou un répertoire. Relatif à quoi ? Relatif à la position actuelle de votre fichier Python .py.

Vous allez créer la structure suivante en créant les dossiers nécessaires puis en y collant les fichiers au bon endroit. Attention, les trois fichiers texte portent le même nom mais ne contiennent pas la même chose. Placez les correctement au bon endroit :

votre dossier de travail

fichier_1.txt

dossier_1

fichier_1.txt

test_chemin.py

dossier_2

fichier_1.txt

Le fichier Python test_chemin.py est le suivant :

#!/usr/bin/env python

# -*- coding: utf-8 -*-

obj_fichier = open("fichier_1.txt","r") # On crée un objet-fichier

while 1 : # On rentre dans la boucle

    ligne = obj_fichier.readline() # On tente de lire la ligne

    if not(ligne) :

        break # On sort si on ne peut pas lire de ligne

    else :

        print(ligne) # On affiche la ligne si on peut la lire

obj_fichier.close() # On ferme le fichier

input("pause")

11° Créer la structure proposée et placer les fichiers texte téléchargés au bon endroit dans votre structure puis créer le fichier python en le plaçant dans le bon dossier.

12° Lancer le programme. Ouvre-t-il bien le fichier texte qui est directement à côté de lui à l'aide du chemin "fichier_1.txt".

Si on veut explorer un sous-dossier à partir du dossier courant, il faut lui donner le nom du sous-dossier juste avant le nom du fichier à ouvrir : "dossier_2/fichier_1.txt" veut dire d'aller ouvrir le dossier dossier_2 à partir de la localisation courante et d'y chercher le fichier fichier_1.txt.

13° Modifier le programme pour qu'il lise le fichier texte contenu dans dossier_2 plutôt que celui-ci de dossier_1.

Et si on veut remonter dans la structure plutôt que de rester dans le répertoire courant ? Il suffit d'utiliser la notation "point point" : "../fichier_1.txt".

14° Tenter d'ouvrir le dernier fichier texte, celui qui est hors de dossier_1.

Bon, avoir trois fichiers avec le même nom ce n'est pas très malin, mais ça vous permet de voir que donner le chemin d'accès lors de l'ouverture d'un chemin est très important.

15° Renommer vos fichiers comme ci-dessous :

votre dossier de travail

fichier_0.txt

dossier_1

fichier_1.txt

test_chemin.py

dossier_2

fichier_2.txt

Nous allons voir maintenant comment utiliser non pas un adressage relatif par rapport au repértoire courant mais comment utiliser l'adressage direct.

Tout d'abord, commençons par trouver votre répertoire courant à la méthode getcwd pour get current working directory. Il s'agit d'une méthode du module os. On écrira donc os.getcwd(). On obtient en retour une chaîne de caractères contenant l'adresse absolue du répertoire.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os

rep_courant = os.getcwd()

print(rep_courant)

os.system("pause")

16° Utiliser ce code pour déterminer le chemin de votre répertoire courant, celui où se trouve votre programme Python normalement. Vous venez d'afficher l'adresse absolue de votre répertoire : on part du nom du disque et on énumère un par un les dossiers et sous-dossiers à ouvrir pour atteindre votre programme Python.

On notera que la valeur fournie par getcwd() est un string. Si vous ne me croyez pas, vous pouvez rajouter print(type(rep_courant)).

Windows utilise les \ plutôt que les / pour séparer les différents dossiers. Je vous conseille néanmoins de garder /, Python se chargera d'expliquer à votre système d'exploitation ce qu'il doit en faire.

17° Trouver ce même chemin en utilisant l'explorateur de votre système d'exploitation.

18° Maintenant que vous avez trouvé l'adresse absolue de votre répertoire, ouvrez le fichier_3.txt en donnant l'adresse directe à Python : commencez par le nom de votre disque et remontez le fil des sous-dossiers. Vous devriez avoir une adresse du type obj_fichier = open("C:/dossier_travail/dossier_1/dossier_2/fichier_2.txt","r").

Remarquez la présence du slash / devant le premier dossier : cela indique qu'on a un chemin absolu et non pas relatif.

Bien. Nous savons ouvrir un fichier, déterminer son adresse absolue ou relative. Il nous reste à voir un point : comment faire la liste des fichiers dans un répertoire. Et oui, si nous savons faire ça, nous pourrions modifier toutes les images du répertoire (les mettre à la même taille par exemple), ou lire le contenu de tous les fichiers texte du répertoire. Plutôt intéressant en terme d'automatisme non ?

4 - Gestion des répertoires

Nous avons vu qu'on peut récupérer le répertoire courant à l'aide du rep_courant = os.getcwd(). Et si nous cherchions à lister ce qu'il contient ? Pour cela, nous allons utiliser une autre fonction contenue dans os : la fonction listdir(adresse_repertoire) qui va renvoyer une liste contenant le contenu du répertoire dont on donne l'adresse sous forme de string. Le code ressemble donc à os.listdir(rep_courant).

Il ne reste qu'à parcourir la liste à l'aide d'une boucle for.

19° Tester le programme ci-dessous. Modifier éventuellement le programme si vous voulez étudier un autre répertoire.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os

# PARTIE 1

print("-- 1 CREATION DE LA LISTE, objet de class list --")

rep_courant = os.getcwd()

liste = os.listdir(rep_courant)

print(liste)

print(type(liste))

# PARTIE 2

print("-- 2 DEBUT DE LA LECTURE DE LISTE --")

for x in liste :

    print(x)

    print(type(x))

os.system("pause")

On voit qu'on crée liste, un objet contenant un ensemble d'autres éléments. Les éléments dans la partie 1 de l'affichage, vous permettent de constater que la liste est bien fournie entre crochets et qu'il s'agit d'une classe list en anglais.

La boucle, dans la partie 2, vous permet de voir que les éléments x de la liste sont des strings contenant le nom du fichier ou du dossier.

Le problème est qu'on mélange les dossiers et les fichiers...

Pour aller plus loin, nous allons avoir besoin de séparer les dossiers d'un côté et les fichiers de l'autre. Pour cela, on peut utiliser la méthode isdir() sur les éléments x de la liste du répertoire : os.path.isdir(x) renvoie True si x est un dossier. Et pour les fichiers ? Il existe une méthode isfile() qui s'utilise de la même façon : os.path.isfile(x) renvoie True si x est un fichier.

20° Tester le programme ci-dessous, plus complet et dans lequel j'ai enlevé les prints inutiles.:

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os

# PARTIE 1

rep_courant = os.getcwd()

liste = os.listdir(rep_courant)

# PARTIE 2

for x in liste :

    if os.path.isfile(x) : # True si x est un fichier

        print("FICHIER : "+x)

    elif os.path.isdir(x) : # True si x est un dossier

        print("DOSSIER : "+x)

    else :

        print("BON, LA, JE NE SAIS PAS VRAIMENT... : "+x)

os.system("pause")

Oui, c'est bien beau, mais on ne distingue pas les fichiers .txt, les fichiers .jpg ... Comment ne garder que les .txt par exemple ?

Et bien, x est une objet de classe str. On peut donc utiliser une multitude de méthodes sur x, dont endswith('.txt') qui renvoie True si le string sur lequel on l'applique fini par les caractères ".txt". C'est pratique pour trier.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os

rep_courant = os.getcwd()

liste = os.listdir(rep_courant)

for x in liste :

    if os.path.isfile(x) : # True si x est un fichier

        if x.endswith('.txt'): # True si x est un fichier txt

            print("FICHIER TXT : "+x)

        elif x.endswith('.jpg') : # True si x est un fichier jpg

            print("FICHIER IMAGE JPEG : "+x)

        elif x.endswith('.png') :

            print("FICHIER IMAGE PNG : "+x)

        else :

            print("FICHIER D'UN AUTRE TYPE : "+x)

    elif os.path.isdir(x) :

        print("DOSSIER : "+x )

    else :

        print("BON, LA JE NE SAIS PAS VRAIMENT")

os.system("pause")

21° Créer un programme qui affiche le contenu de tous les fichiers texte contenus dans un répertoire. Testez le en plaçant vos trois fichiers (texte) de votre choix dans un unique répertoire. Pensez à bien refermer le fichier en cours avant d'ouvrir le suivant.

votre dossier de travail

fichier_1.txt

dossier_1

fichier_1.txt

fichier_2.txt

fichier_3.txt

test_chemin.py

dossier_2

fichier_1.txt

Comme vous le voyez, nous savons maintenant sélectionner un ensemble de fichiers et agir sur chacun d'entre eux de façon identiques. Nous pourrions ainsi reprendre les projets sur les images (changer les couleurs, pixeliser, changer les dimensions...) et modifier automatiquement tous les fichiers-images contenus dans un répertoire. Il suffit de s'inspirer des codes précédents.

A vous de voir si vous désirez en faire l'un de vos projets...

5 - Choix d'un mot dans un fichier avec le module RANDOM

Nous allons utiliser ces nouvelles notions sur un autre projet : le jeu du pendu.

Si vous vous souvenez, on demandait en début de partie le mot à découvrir ou on utilisait le mot par défaut "Python". Mais on pourrait faire mieux : on pourrait créer un fichier contenant une multitide de mots et en choisir un au hasard.

Pour obtenir un élément d'une liste au hasard, c'est facile. Il faut importer le module random et utiliser la fonction randint.

Son utilisation est facile : i = random.randint(a, b) permet d'obtenir un nombre entier allant de a (inclus) à b (inclus).

La ligne de code i = random.randint(0, len(liste_mots) - 1) va créer un integer au hasard, integer compris entre 0 et la longeur de la liste (on rajoute -1 car le premier élément de la liste est numéroté 0, vous commencez à avoir l'habitude).

22° Télécharger le petit fichier texte ( ici ) contenant un mot par ligne et compléter le code suivant pour qu'il choisisse l'un des mots au hasard.

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os

import random # Nouveau : pensez à l'importer


# Etape 1 - Récupération des mots dans le fichier

obj_fichier = open("fichier_mots.txt","r")

liste_mots = obj_fichier.readlines()

obj_fichier.close() # Fermeture du fichier


# Etape 2 - Choix d'un mot au hasard dans la liste

i = ...

mot_choisi = ...

print(mot_choisi)

os.system("pause")

Sinon, il existe en fait une méthode toute faite pour aller chercher un élément au hasard dans une liste : choice

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import os

import random


# Etape 1 - Récupération des mots dans le fichier

obj_fichier = open("fichier_mots.txt","r")

liste_mots = obj_fichier.readlines()

obj_fichier.close() # Fermeture du fichier


# Etape 2 - Choix d'un mot au hasard dans la liste

mot_choisi = random.choice(liste_mots)

print(mot_choisi)

os.system("pause")

Allez voir la bibliothèque random si vous avez besoin de faire un truc bizarre avec les tirages au hasard. Il existe déjà certainement une méthode qui peut le faire pour vous (tirer une carte au hasard dans un paquet sans risquer de la retirer avant que le paquet ne soit utilisé intégralement...)

Il reste encore un problème à gérer néanmoins. Si vous observez bien votre print final en en mettant deux à la suite par exemple, vous vous rendriez compte que la chaine de caractère ne contient pas que le mot "clavier" par exemple : elle contient "clavier\n" puisqu'on a récupéré le contenu ligne par ligne. On a donc gardé le saut de ligne.

Comment faire pour s'en séparer ? Une possibilité est d'utiliser la méthode replace : un x = x.replace("\n","") devrait faire l'affaire. Pourquoi supprimer le saut de ligne alors qu'il ne se voit pas à l'écran ? Et bien, nous allons utiliser vos nouvelles connaissances pour le jeu du pendu un peu plus bas. Or, nous allons vouloir comparer deux chaînes de caractères. S'il existe des caractères cachés dans l'une des deux chaînes, le code risque de ne pas fonctionner correctement sans qu'on sache pourquoi...

23° Modifiez votre programme précédent pour que votre string mot_choisi ne contienne pas de saut de ligne.

Mini-projet optionnel de fin : le jeu du pendu avec Tkinter et le choix d'un mot dans un fichier

Voilà, nous pouvons donc passer à l'étape finale : nous allons utiliser le fichier texte contenant un mot par ligne pour en tirer un au hasard de façon à rendre le jeu du pendu plus intéressant pour quelqu'un jouant seul.

Je vous donne le code de l'interface graphique ci-dessous et nous allons vous donner l'essentiel : quel objet contient quoi et comment faire pour le modifier ou le récuperer.

Attention, ce code n'est pas encore optimisé, il possède de nombreuses déclarations inutiles ect... Nous l'utiliserons encore par la suite en exemple dans la partie "Interface graphique 2". Je vous montrerai à ce moment comment écrire certaines choses de façon plus synthétique et moins gourmande en mémoire.

#!/usr/bin/env python

# -*- coding: utf-8 -*-


# ###################################

# Importation des modules nécessaires

# ###################################

from tkinter import * # Importation des classes et méthodes sans avoir besoin de noter .tkinter

from tkinter.filedialog import * # pour les gestions de fichiers

import random # Importation des classes et méthodes en exigeant de noter random.methode ou random.classe


# Fonction qui définit le mot secret à partir d'un ensemble de mots contenus dans un fichier

def dictionnaire() :

    filepath = askopenfilename(title = "Ouvrir un fichier texte", filetypes = [('fichier txt','.txt')])

    # Etape 1 - Récupération des mots dans le fichier

    obj_fichier = ...

    liste_mots = ...

    ...

    obj_fichier.close() # Fermeture du fichier

    # Etape 2 - Choix d'un mot au hasard dans la liste

    mot_choisi = ...

    ...

    motSecret.set(...) # Nouveau contenu de motSecret ce qui va automatiquement changer l'affichage


# ########################################

# Création de la fenètre et de ses widgets

# ########################################


# Création de la fenetre

fen_princ = Tk() # Objet-fenetre principal


# Widget du titre (label fixe)

monTitre = Label(fen_princ, text = "JEU DU PENDU" ,font = ("Helvetica", 15), height = 3, width = 60, anchor = N)

monTitre.pack()


# Widget de texte informatif (label fixe)

monAffichage1 = Label(fen_princ, text = "Tapez votre mot secret ou ouvrez un fichier-texte : " , font = ("Helvetica", 10), height = 3, width = 50, anchor = W)

monAffichage1.pack()


# Widget Button pour lancer le chargement d'un mot dans le dictionnaire

boutonDico = Button(fen_princ, text = "Charger un fichier de mots", command = dictionnaire)

boutonDico.pack()


# Widget Entry permettant de rentrer le mot secret choisi à la main ou de le changer

motSecret = StringVar()

motSecret.set("PYTHON")

eSecret = Entry(fen_princ, textvariable = motSecret)

eSecret.configure(show = "*")

eSecret.pack()


# Lancement de la boucle de surveillance sur la fenetre principale

fen_princ.mainloop()

Que fait ce code ?

Premièrement, il importe les bibliothèques nécessaires au bon fonctionnement de la fenètre : tkinter et tkinter.filedialog pour l'interface graphique et l'interface de choix de fichier, random pour la gestino de l'aléatoire.

from tkinter import * # Importation des classes et méthodes sans avoir besoin de noter .tkinter

from tkinter.filedialog import * # pour les gestions de fichiers

import random # Importation des classes et méthodes en exigeant de noter random.methode ou random.classe

Ensuite, il contient une fonction dont on donne la définition (def dictionnaire() :), que vous allez devoir compléter : c'est ici qu'on va ouvrir la fenêtre de choix de fichier et qu'on va choisir le nouveau mot secret. Comme nous l'avons vu dans le chapitre 6, il faut utiliser la méthode askopenfilename pour ouvrir la fenêtre de dialogue-fichier. On mettra le fichier désigné par l'utilisateur dans filepath pour parler un peu anglais. Vous auriez pu le nommer chemin_fichier ou fichier, ça fonctionnerait aussi.

def dictionnaire() :

    filepath = askopenfilename(title = "Ouvrir un fichier texte", filetypes = [('fichier txt','.txt')])

    # Etape 1 - Récupération des mots dans le fichier

    ...

Le reste sera décrit juste après avoir vu de quoi est composée la fenètre.

Enfin, il fournit le contenu de l'interface graphique :

24° Compléter la fonction dictonnaire de façon à ce que :

Il ne vous restera qu'à affecter une nouvelle valeur à motSecret, le VarString associé au widget eSecret.

Bien, nous avons vu l'essentiel. Il vous reste à voir si ce n'est pas encore fait la fiche sur les encodages. Cela vous permettra de comprendre pourquoi certains fichiers texte ne sont pas lu correctement. Nous reviendrons sur l'encodage (et les fichiers texte) dans l'une des leçons suivantes. La fiche sur la gestion des erreurs sur les fichiers pour être instructive également.

Vous verrez notamment qu'il faut faire attention avec l'encodage des fichiers de type texte. Notepad++ est souvent configuré par défaut sur UTF-8 with BOM : cet encodage rajoute un caractère invisible en début de fichier.

Ce caractère est utile pour les encodages UTF-16 et UTF-32. Mais si vous n'en avez pas l'utilité, convertissez plutôt vos fichiers en UTF-8 sans BOM avant enregistrement. C'est possible avec Notepad.