On va montrer comment on peut utiliser python pour interagir avec des fichiers.
La principale fonction qui va nous servir dans cette partie est open
. Les deux arguments qui vont nous intéresser sont
file
qui précise le nom du fichier avec lequel on veut interagirmode
qui précise l’interaction que l’on veut avoir avec ce fichier. Typiquement on choisira entre les modes r
(read) pour lire le fichier, w
(write) pour écrire le fichier (en effaçant au préalable son contenu) et a
(append) pour écrire à la suite du fichier. Il y en a en fait d’autres mais qu’on utilise moins.REMARQUE notez que pour les deux modes d’écritures, on crée le fichier si jamais il n’existe pas.
Si je me place dans un répertoire contenante un seul fichier essai
dont le contenu est
Première ligne.
Deuxième ligne.
Et une troisième.
alors on peut avoir la session:
>>> fichier = open("essai", "r")
>>> type(fichier)
<class '_io.TextIOWrapper'>
>>> dir(fichier)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
>>> fichier.close()
ATTENTION notez bien que lorsqu’on ouvre un fichier de cette façon il faudra bien penser à le fermer avec la méthode close
lorsqu’on en aura plus besoin. On verra pourquoi plus loin ainsi qu’une syntaxe alternative.
REMARQUE le type de l’objet renvoyé par open
est relativement complexe, notons juste que le IO
signifie InputOutput
. Cet objet possède de nombreuses méthodes mais on se concentrera sur read
et readlines
pour la lecture, write
pour l’écriture. Notez qu’elles sont toutes présentes même si le fichier est ouvert officiellement en mode lecture!
Un fichier ouvert en lecture est un itérable et on récupère alors les lignes.
>>> for ligne in fichier:
... print(ligne)
...
Première ligne.
Deuxième ligne.
Et une troisième.
Notez que l’on a le saut de ligne de la chaine et celui de print
qui se juxtaposent. Pour enlever les caractères “blancs” en début et en fin de str
on peut utiliser la méthode strip
.
>>> fichier = open("essai", "r")
>>> lignes = [ligne.strip() for ligne in fichier]
>>> fichier.close()
>>> lignes
['Première ligne.', 'Deuxième ligne.', 'Et une troisième.']
On peut aussi utiliser readlines
pour récupérer la liste des lignes (ces dernières étant des str
, et gardant leur caractère de saut de ligne \n
)
>>> fichier = open("essai", "r")
>>> fichier.readlines()
['Première ligne.\n', 'Deuxième ligne.\n', 'Et une troisième.\n']
>>> fichier.readlines()
[]
>>> fichier.close()
ATTENTION notez bien qu’après la première lecture c’est comme si le curseur imaginaire était en bout de fichier. Un nouvel appel à la méthode readlines
renvoie donc une liste vide!
Finalement mentionnons que la méthode read
permet de tout récupérer en une chaine de caractères.
>>> fichier = open("essai", "r")
>>> texte = fichier.read()
>>> fichier.close()
>>> texte
'Première ligne.\nDeuxième ligne.\nEt une troisième.\n'
ATTENTION lorsque les fichiers sont trop gros on préfèrera utiliser la toute première version pour gérer les lignes les unes après les autres.
Il y a des possibilités de lecture plus fines, basées sur la manipulation du curseur imaginaire mais les précédentes suffisent dans la majorité des cas. Vous pouvez regarder help(fichier.seek)
pour plus d’informations.
Pour écrire on utilisera la méthode write
à laquelle on passe une chaine de caractère.
>>> fichier = open("nouveau", "w")
>>> fichier.write("première ligne")
14
>>> fichier.write("deuxième ligne")
14
>>> fichier.close()
>>> fichier_bis = open("nouveau", "r")
>>> print(fichier_bis.read())
première lignedeuxième ligne
>>> fichier_bis.close()
ATTENTION on n’oubliera pas d’insérer des sauts de ligne si jamais on en veut.
ATTENTION notez bien la différence entre w
:
>>> ecriture = open("nouveau", "w")
>>> ecriture.write("Premier passage\n")
16
>>> ecriture.close()
>>> ajout = open("nouveau", "w")
>>> ajout.write("Deuxième passage.\n")
18
>>> ajout.close()
>>> lecture = open("nouveau", "r")
>>> print(lecture.read())
Deuxième passage.
>>> lecture.close()
et a
>>> ecriture = open("nouveau", "w")
>>> ecriture.write("Premier passage\n")
16
>>> ecriture.close()
>>> ajout = open("nouveau", "a")
>>> ajout.write("Deuxième passage.\n")
18
>>> ajout.close()
>>> lecture = open("nouveau", "r")
>>> print(lecture.read())
Premier passage
Deuxième passage.
>>> lecture.close()
REMARQUE la méthode write
renvoie le nombre de caractères écrits.
L’objet renvoyé par open
est en fait un buffer: une zone mémoire intermédiaire entre le disque et python. Ceci afin d’optimiser les opérations d’écriture sur disque qui prennent beaucoup plus de temps que l’accès en mémoire vive.
En particulier toutes les écritures dans le buffer ne sont pas instantanément répercutées sur le disque. Il peut y avoir attente
close
.Or si python plante entre temps ou bien qu’on effectue des écritures/lectures en parallèles, le buffer peut ne pas être recopié sur le disque à temps.
>>> ecriture = open("test_buffer", "w")
>>> ecriture.write("Bien écrit")
10
>>> lecture = open("test_buffer", "r")
>>> print(lecture.read())
>>> lecture.close()
>>> ecriture.close()
>>> lecture = open("test_buffer", "r")
>>> print(lecture.read())
Bien écrit
>>> lecture.close()
Python propose une syntaxe pour assurer que le fichier soit fermé quoi qu’il arrive. Il s’agit de l’instruction composée with
qui crée un gestionnaire de contexte (objet très général mais que l’on ne verra que dans ce cas particulier).
>>> with open("nouveau", "r") as fichier:
... print(fichier.read())
...
Premier passage
Deuxième passage.
Le point clef est qu’à la fin du corps principal de l’instruction with
, le fichier sera fermé, même si une erreur a été soulevée.
Pour naviguer plus facilement dans l’arborescence de fichier et faciliter le portage du code entre différents systèmes (Linux
, OSX
, Windows
). On va montrer comment utiliser la bibliothèque pathlib
.
On se place dans un répertoire avec l’arborescence suivante:
.
├── autre
│ └── alt.txt
├── essai
├── nouveau
├── rep
│ ├── autre.txt
│ ├── encore.txt
│ ├── fichier1
│ ├── fichier2
│ └── fichier3
└── test_buffer
On peut alors naviguer de la façon suivante.
>>> from pathlib import Path
>>> racine = Path(".")
>>> print(racine)
.
>>> racine = racine.resolve()
>>> print(racine)
/home/vincent/Code/python/demo_repertoire/foad
>>> type(racine)
<class 'pathlib.PosixPath'>
racine
en appelant Path
, la chaine "."
représente la position courante.Path
a crée un objet de type PosixPath
mais il existe aussi WindowsPath
et d’autres possibilités. L’intérêt est que le distinction est gérée automatiquement.resolve
pour passer d’un chemin relatif à un chemin absolu partant du plus bas possible dans l’arborescence.On peut maintenant crée de nouveau objets Path
représentant des répertoires ou des fichiers (ce qu’on peut détecter) de la façon suivante.
>>> from pathlib import Path
>>> racine = Path(".").resolve()
>>> sous_rep = racine / "rep"
>>> sous_rep.is_dir()
True
>>> sous_rep.is_file()
False
>>> sous_rep
PosixPath('/home/vincent/Code/python/demo_repertoire/foad/rep')
>>> chemin_fichier = racine / "essai"
>>> chemin_fichier.is_file()
True
>>> chemin_fichier.is_dir()
False
>>> chemin_fichier
PosixPath('/home/vincent/Code/python/demo_repertoire/foad/essai')
ATTENTION windows utilise des \
plutôt que des /
dans l’arborescence mais on utilisera toujours des /
entre objets Path
et str
.
ATTENTION rien n’empêche de créer un chemin qui n’existe pas mais on peut tester cette propriété grâce à la méthode exists
.
>>> from pathlib import Path
>>> racine = Path(".")
>>> fantaisiste = racine / "blabla"
>>> fantaisiste.exists()
False
>>> racine.exists()
True
On peut faire des recherches de chemins avec la méthode glob
. Celle-ci accepte des *
pour permettre la recherche de motifs et **
pour des répertoires.
>>> from pathlib import Path
>>> racine = Path(".")
>>> list(racine.glob("*"))
[PosixPath('autre'), PosixPath('test_buffer'), PosixPath('nouveau'), PosixPath('rep'), PosixPath('essai')]
>>> list(racine.glob("rep/*"))
[PosixPath('rep/encore.txt'), PosixPath('rep/autre.txt'), PosixPath('rep/fichier3'), PosixPath('rep/fichier2'), PosixPath('rep/fichier1')]
>>> list(racine.glob("rep/fichier*"))
[PosixPath('rep/fichier3'), PosixPath('rep/fichier2'), PosixPath('rep/fichier1')]
>>> list(racine.glob("**/*.txt"))
[PosixPath('autre/alt.txt'), PosixPath('rep/encore.txt'), PosixPath('rep/autre.txt')]
Concluons cette très brève introduction à pathlib
en mentionnant que l’on peut fournir un objet Path
à open
pour passer d’un chemin au fichier qu’il représente.
>>> from pathlib import Path
>>> racine = Path(".")
>>> list(racine.glob("*"))
[PosixPath('autre'), PosixPath('test_buffer'), PosixPath('nouveau'), PosixPath('rep'), PosixPath('essai')]
>>> chemin = racine / "essai"
>>> with open(chemin, "r") as fichier:
... print(fichier.read())
...
Première ligne.
Deuxième ligne.
Et une troisième.
>>> for chemin in racine.glob("**/*.txt"):
... with open(chemin, "a") as fichier:
... fichier.write(f"ceci est {chemin}")
...
25
26
25
>>> for chemin in racine.glob("**/*.txt"):
... with open(chemin, "r") as fichier:
... print(fichier.read())
...
ceci est autre/alt.txt
ceci est rep/encore.txt
ceci est rep/autre.txt
Pour l’ouverture de fichiers:
>>> help(open)
Pour la navigation consulter la documentation.