Table des matières

, , ,

Manipulation des fichiers en python

Pour manipuler aisément les fichiers il faut avoir abordé 3 notions:

É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 JSON1) ou CSV 2)

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

1)
JavaScript Object Notation
2)
Comma Separated Values