{{tag>dev python fichier file}}
====== Manipulation des fichiers en python ======
Pour manipuler aisément les fichiers il faut avoir abordé 3 notions:
* l'encodage
* l'itération
* le context manager
===== Écrire du texte dans un fichier =====
Pour lire et écrire du texte dans les fichiers, il faut spécifier correctement l'encodage. L'objet fichier est créé avec la fonction built-in open(), il se chargera d'encoder/décoder les caractères en fonction de l'encodage choisit:
file = open('/path/to/file.txt', 'w', encoding='utf8')
for cpt in range(100):
file.write(f"ligne {cpt:4d}\n")
# l'appel de la méthode close() est important
file.close()
===== Lire un fichier texte =====
En python les fichiers sont des itérateur: ils peuvent être directement intégrés à une boucle for:
file = open('/path/to/file.txt', 'r', encoding='utf8')
for line in file:
# le fichier est parcouru ligne par ligne
# transformer la str en liste
line = line.split()
# la liste étant mutable on peut effectuer nos traitements
...
file.close()
===== Le context manager =====
Le code précédent ne garantit pas que la ressource fichier soit bien libérée: exception à l’exécution ou oubli d'appel pas la méthode close(). Le protocole de context manager assurer l'ensemble des opérations de libération des ressources auprès du système d'exploitation lorsque un objet python n'est plus utilisé. L'objet fichier implémente ce protocole, pour l'utiliser il faut le placer après l'instruction **with**:
with open('/path/to/filename.txt') as file:
# bloc d'instruction lié au context manager
for line in file:
print line
===== Ecrire un flux binaire dans un fichier =====
with open('/path/to/filename.txt', 'bw') as file:
# bloc d'instruction lié au context manager
for line in range(100):
file.write(b'\xff')
# l'objet doit être de type bytes, pour convertir un int
# c = 0xff.to_bytes(1,byteorder='little',signed=False)
Pour vérifier le contenu du fichier, on peut utiliser **od**:
#affiche le flux par valeurs hexadécimales de 1 octet interprétation des valeurs
# en little endian
od --address-radix=d --width=10 --output-duplicates --format=x1 --endian=little /tmp/test.raw
0000000 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0
0000010 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0
0000020 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0
0000030 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0
0000040 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0
0000050 f0 f0 f0 f0 f0 f0 f0 f0 f0 f0
0000060
===== Les modes d'ouvertures =====
Les modes les plus utilisés:
^ Mode ^ Description |
| "r" | read, lecture |
| "w" | write, écriture. Si un contenu existait il est écrasé. |
| "a" | append, ajout. Ecriture en fin de fichier. Conserve le contenu préexistant |
Le détail des modes d'ouvertures existant est intégré à la documentation de la fonction built-in open().
===== Méthodes bas niveau =====
==== Méthode read() ====
La méthode **read()** permet de lire dans un fichier un buffer d'une taille choisit. Si la taille du buffer n'est pas spécifiée, c'est tout le fichier qui est retourné.
with open('/path/to/filename.txt', 'bw') as file:
# bloc d'instruction lié au context manager
all_content = file.read()
print(f"contenu complet:\n {all_content}", end="")
with open('/path/to/filename.txt', 'bw') as file:
# bloc d'instruction lié au context manager
for bloc in range(10):
#lecture par bloc de 4 caractères
print(f"bloc ID {bloc}: {repr(file.read(4))}")
==== Méthode flush() ====
Pour des raisons de performances, le système d'exploitation peut différer les demandes de lectures/écritures en plaçant les données dans des tampons. Dans certains cas ce comportement par défaut peut être génant: la méthode flush() permet de vider les tampons et forcer immédiatement la lecture/écriture.
===== Le module pathlib =====
Pour opérer sur les chemins, nom de fichiers présents sur l'arborescence du système d'exploitation il y avait les module os et glob aujourd'hui dépréciés.
On utilisera plutôt **pathlib**:
filename = '/tmp/test.txt'
from pathlib import Path
path = Path(filename)
# retourne True si le fichier existe
path.exists()
# retourner un tuple contenant entre autre propriétaire, taille, date d'accès
path.stat()
file_size = path.stat().st_size()
# Détruire un fichier:
path.unlink()
# rechercher des fichiers
dirpath = Path('./data/')
for files in dirpath.glob("*.txt"):
print(file)
===== Formats =====
Lors de l'execution d'un programme python les données en mémoire ont une représentation qui dépend de l'architecture machine. Cette forme bien adaptée pour le traitement et les calcul sur l'hote n'est pas forcement pertinante lorsqu'il sagit de stocker ou transmettre l'information. Il convient donc de faire de la traduction dans les deux sens entre d'une part la représentation en mémoire et d'autre part la représentation sur le réseau ou les disques de stockage. Pour cela on utilise divers encodages (marshalling) comme **JSON**((**J**ava**S**cript **O**bject **N**otation)) ou **CSV** ((**C**omma **S**eparated **V**alues))
JSON s'est popularisé car il est léger, permet de communiquer avec des applications web en JavaScript et qu'il est supporté par de nombreux langage. Il permet de sérialiser facilement la plupart des types de base:
dataset = [10,33,
'ma chaine',
[2.5,3.1],
{'nom':'Doe', 'prenom':'John', 'age':33},
# les tuples seront convertis en listes
(2,3,4)
]
# Ecrire dans un fichier au format JSON
with open("/tmp/test.json", 'w', encoding='utf8') as fs_json:
json.dump(dataset, fs_json)
del(dataset)
# Relire, récupérer des données au format JSON
with open("/tmp/test.json", 'r', encoding='utf-8') as json_input:
dataset = json.load(json_input)
dataset
[10,
33,
'ma chaine',
[2.5, 3.1],
{'nom': 'Doe', 'prenom': 'John', 'age': 33},
[2, 3, 4]]
Les données une fois rechargées ne comportent plus le tuple initial, il a été remplacé par une liste équivalente. Certains types de base Python non natif en JavaScript ne sont pas supportés par JSON: tuple, complex, set, frozenset
Le module **pickle** offre un format proche de JSON intégrant les types de base et permettant de faire des sauvegardes locales d'objets. Pour enregistrer facilement des objets Python il peut être très utile.
===== Entrées sorties standards =====
Les fichiers d'entrées/sorties standards sont exposés par Python via le module sys
import sys
for channel in (sys.stdin, sys.stdout, sys.stderr):
print(channel, f" at @{id(channel):0x}")
Comme sys.stdout est une variable du module sys référençant un objet fichiere, on peut lui faire référencer un autre fichier et rediriger ainsi les sorties.
===== Références =====
* https://openclassrooms.com/courses/apprenez-a-programmer-en-python/les-fichiers-2
* https://stackoverflow.com/questions/11555468/how-should-i-read-a-file-line-by-line-in-python#11555509