Initial commit
This commit is contained in:
410
documentation/08-text-files.md
Normal file
410
documentation/08-text-files.md
Normal file
@ -0,0 +1,410 @@
|
||||
---
|
||||
title: Manipuler des fichiers texte
|
||||
author: Steve Kossouho
|
||||
---
|
||||
|
||||
# Découvrir comment manipuler des fichiers texte
|
||||
|
||||
----
|
||||
|
||||
## Écrire et lire des fichiers (version de base)
|
||||
|
||||
Python vous offre, sans import nécessaire, une fonction `open`{.python} vous permettant d'ouvrir des
|
||||
fichiers pour les manipuler. Cette fonction semble provenir du module `io`{.python} de la
|
||||
bibliothèque standard.
|
||||
|
||||
Pour ouvrir un fichier en écriture :
|
||||
|
||||
```python {.numberLines}
|
||||
file = open("fichier.txt", "w", encoding="utf-8") # Ouvrir le fichier
|
||||
file.write("Texte à écrire\nDeuxième ligne.\n")
|
||||
file.write("Troisième ligne.")
|
||||
file.close() # Toujours penser à le refermer, pour votre système
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
La fonction `open()`{.python} prend quelques arguments importants :
|
||||
|
||||
- `filename` : chemin de fichier relatif (au répertoire de travail) ou absolu
|
||||
- `mode` : `"w"` pour write, `"r"` pour read, `"a"` pour append
|
||||
- `encoding`: rendez-le explicite, et "utf-8" si possible
|
||||
|
||||
----
|
||||
|
||||
### Pages de code les plus fréquemment rencontrées :
|
||||
|
||||
1. `utf-8` : ±90% du temps (complètement rétrocompatible avec `ASCII`)
|
||||
2. `windows-1252` ou `1252` : ±5% du temps (provient de Windows 95)
|
||||
3. `latin-1` : ±5% du temps (aussi nommé `iso8859-1`)
|
||||
4. `utf-16` : certains pays n'utilisant quasiment pas de caractères ASCII, ils produisent souvent de l'UTF-16 (ex. Chine, Russie)
|
||||
|
||||
----
|
||||
|
||||
### Plus : Échappement de caractères
|
||||
|
||||
- Échappements classiques 0 à 8 bits (_0 à 255_)
|
||||
- Échappements de caractères 0 à 16 bits (_256 à 65535_)
|
||||
- Échappements de caractères 0 à 32 bits (_65536 et plus_)
|
||||
|
||||
```python {.numberLines}
|
||||
print("\x41 = \101 = A") # codes 8 bits, 2 caractères obligatoires
|
||||
print("\u00E6 = æ") # codes 16 bits, 4 caractères obligatoires
|
||||
print("\U0001F44D = 👍️") # codes 32 bits, 8 caractères obligatoires
|
||||
```
|
||||
|
||||
[Algorithme UTF-8](https://en.wikipedia.org/wiki/UTF-8)
|
||||
|
||||
----
|
||||
|
||||
### Ouvrir un fichier en lecture
|
||||
|
||||
```python {.numberLines}
|
||||
file = open("fichier.txt", "r", encoding="utf-8")
|
||||
line1 = file.readline() # Lit une ligne, et avance le curseur à la ligne suivante
|
||||
rest1 = file.read() # Lit le fichier jusqu'à la fin
|
||||
file.close() # Toujours penser à le refermer, pour votre système
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
### Outils disponibles pour gérer des fichiers
|
||||
|
||||
Si l'on utilise des éditeurs de texte, on pourrait penser qu'il est facile de demander à Python de, par exemple :
|
||||
|
||||
```{.text .numberLines}
|
||||
- Remplacer une ligne
|
||||
- Lire une ligne arbitraire d'un document
|
||||
- Supprimer une ligne
|
||||
- Remplacer du texte
|
||||
```
|
||||
|
||||
Aucune de ces fonctionnalités n'existe en standard, dans aucun langage; les logiciels qui vous permettent
|
||||
ces manipulations sont obligés, souvent, de conserver une copie du document en mémoire (quand c'est possible)
|
||||
et d'effectuer les manipulations en mémoire avant de réécrire tout ou partie du fichier.
|
||||
|
||||
Tout ce qu'on a d'utile, c'est :
|
||||
|
||||
```{.text .numberLines}
|
||||
- Ajouter du contenu à la fin
|
||||
- Réécrire le fichier
|
||||
- Lire ligne par ligne du début, jusqu'à arriver à une ligne arbitraire
|
||||
- Déplacer le curseur à une position en octets
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
Une bibliothèque externe Python propose des outils pour effectuer certaines de ces actions :
|
||||
|
||||
```bash {.numberLines}
|
||||
pip install textfile
|
||||
```
|
||||
|
||||
[Page PyPI du package textfile](https://pypi.org/project/textfile/)
|
||||
|
||||
----
|
||||
|
||||
## Écrire et lire des fichiers (gestionnaire de contexte)
|
||||
|
||||
Une version raccourcie du `f = open()`{.python} … `f.close()`{.python} consiste à utiliser un gestionnaire de
|
||||
contexte (la formation Python intermédiaire explique le concept). La syntaxe consiste en ce qui suit :
|
||||
|
||||
```python {.numberLines}
|
||||
with open("fichier.txt", "r", encoding="utf-8") as file:
|
||||
# `file.close()` : appelé automatiquement en fin de bloc
|
||||
text = file.read()
|
||||
```
|
||||
|
||||
À la fin de l'exécution du bloc, une méthode spéciale est automatiquement appelée et ferme le descripteur
|
||||
de fichier s'il est resté ouvert. Plus besoin d'exécuter manuellement `file.close()`{.python}
|
||||
|
||||
----
|
||||
|
||||
### Bonus : Ouverture simultanée de fichiers
|
||||
|
||||
Avec un seul bloc `with` … `as`, il est possible d'utiliser plusieurs gestionnaires de contexte en même
|
||||
temps, par exemple, si l'on veut ouvrir un fichier en lecture, faire une conversion de données et les
|
||||
écrire dans un fichier ouvert en écriture, on peut :
|
||||
|
||||
```python {.numberLines}
|
||||
with open("input.txt", "r") as infile, open("output.txt", "w") as outfile:
|
||||
data = infile.read().upper() # tout mettre en majuscules
|
||||
outfile.write(data)
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Parcourir facilement les lignes d'un fichier
|
||||
|
||||
Il est possible, lorsque vous avez un descripteur de fichier ouvert, de parcourir facilement ligne à ligne
|
||||
le contenu de votre fichier texte, en utilisant le mot-clé `for`{.python} :
|
||||
|
||||
```python {.numberLines}
|
||||
with open("fichier.ext", "r", encoding="utf-8") as file:
|
||||
for line in file:
|
||||
print(line)
|
||||
```
|
||||
|
||||
En interne, ici la fonction `open()`{.python} est exécutée en deux temps; un première fois pour renvoyer
|
||||
le descripteur de fichier, et une seconde fois lors de la fin du bloc... Mais c'est un peu magique, et c'est
|
||||
un sujet pour une formation plus avancée de Python.
|
||||
|
||||
----
|
||||
|
||||
## Formats structurés : JSON
|
||||
|
||||
Le **JSON** (JavaScript Object Notation) est un format de texte extrêmement populaire
|
||||
pour partager des données hiérarchiques. Il a largement supplanté le XML dans les systèmes
|
||||
modernes et les services web. Les données contenues dans un fichier texte au format JSON peuvent être
|
||||
interprétées et transformées en données Python, telles que `dictionnaires` et `listes`, `nombres` etc.
|
||||
|
||||
----
|
||||
|
||||
### Lecture et écriture de fichiers JSON
|
||||
|
||||
Il est extrêmement facile de lire ou d'écrire du contenu depuis ou vers un fichier
|
||||
représentant une structure JSON. [Documentation du module JSON](https://docs.python.org/fr/3/library/json.html)
|
||||
|
||||
```python {.numberLines}
|
||||
import json
|
||||
|
||||
# Lire un fichier JSON
|
||||
with open("fichier.json", "r", encoding="utf-8") as file:
|
||||
data = json.load(file)
|
||||
|
||||
# Écrire un fichier JSON, l'objet à écrire doit être une liste ou
|
||||
# un dictionnaire
|
||||
with open("copie.json", "w", encoding="utf-8") as file:
|
||||
json.dump(data, file, indent=2)
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
## Formats structurés : CSV
|
||||
|
||||
Le CSV est un format accessible et non propriétaire pour représenter des données tabulées (en table)
|
||||
. C'est un format texte, utilisant, par défaut, des virgules et des retours à la ligne pour séparer
|
||||
les lignes et colonnes.
|
||||
|
||||
La manipulation des CSV fait aussi partie de la bibliothèque standard de Python. [Documentation CSV](https://docs.python.org/3/library/csv.html)
|
||||
|
||||
----
|
||||
|
||||
### Parcourir les lignes d'un fichier CSV
|
||||
|
||||
Lire le contenu d'un fichier CSV est presque aussi simple que pour un fichier JSON, à quelques détails près :
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
with open("fichier.csv", "r", encoding="utf-8") as file:
|
||||
reader = csv.reader(file, delimiter=",")
|
||||
for row in reader:
|
||||
print(row) # Affiche une ligne sous forme d'une liste de chaînes de caractères
|
||||
```
|
||||
|
||||
Vous ouvrez d'abord votre fichier en lecture et en mode texte, comme tout fichier texte, puis vous créez un objet de type
|
||||
lecteur, configuré pour lire et interpréter le contenu de lignes du document. Par défaut, le lecteur ne reconnaît que les
|
||||
documents dont les cellules sont séparées par la virgule `,` et dont les lignes sont séparées par `\n`.
|
||||
Heureusement, cet objet peut être configuré pour comprendre des fichiers avec des propriétés différentes.
|
||||
|
||||
----
|
||||
|
||||
### Paramètres de formatage d'un fichier CSV
|
||||
|
||||
Pour lire ou écrire un fichier CSV, il est possible de configurer les objets en charge d'interpréter ou de formater le
|
||||
contenu du fichier. En lecture, vous configurerez un objet de type `reader`, et en écriture... `writer`. Dans les deux cas, la procédure sera la même.
|
||||
|
||||
Vous avez trois façons de configurer votre objet lecteur ou écrivain :
|
||||
|
||||
- Soit en passant un objet de type `Dialect`, un objet dont les attributs servent à configurer toutes les facettes du formatage d'un fichier CSV;
|
||||
- soit en passant un nom d'objet de type `Dialect`, par exemple `"excel"` (séparateur virgule);
|
||||
- soit en passant des arguments individuels qui correspondent aux attributs des objets `Dialect`.
|
||||
|
||||
----
|
||||
|
||||
#### Configuration via un objet Dialect
|
||||
|
||||
Pour définir votre propre `Dialect`, vous pouvez créer une classe héritant de `Dialect`, dans laquelle vous n'avez qu'à
|
||||
redéfinir les attributs dont vous souhaitez changer la valeur. Voir [Classe Dialect du module CSV](https://docs.python.org/fr/3/library/csv.html#dialects-and-formatting-parameters)
|
||||
pour connaître les valeurs de base.
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
class MonDialecte(csv.Dialect):
|
||||
"""Dialecte CSV utilisant des // comme séparateur."""
|
||||
delimiter = "//"
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
Une fois que votre dialecte est défini et prêt à l'usage, vous pouvez l'utiliser pour lire ou écrire un fichier :
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
class MonDialecte(csv.Dialect):
|
||||
"""Dialecte CSV utilisant des // comme séparateur."""
|
||||
delimiter = "//"
|
||||
|
||||
with open("fichier.csv", "r", encoding="utf-8") as file:
|
||||
reader = csv.reader(file, dialect=MonDialecte())
|
||||
for row in reader:
|
||||
print(row)
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
Plutôt que de créer une classe de dialecte pour personnaliser la manipulation d'un fichier, il est possible de passer à l'argument `dialect`
|
||||
une chaîne de caractères représentant le nom d'un dialecte prédéfini. Par défaut, les valeurs disponibles sont `"excel"`, `"excel-tab"` et `"unix"`.
|
||||
|
||||
```python {.numberLines}
|
||||
...
|
||||
reader = csv.reader(file, dialect="excel")
|
||||
```
|
||||
|
||||
Vous pouvez aussi enregistrer des noms de dialecte supplémentaires en utilisant la fonction [`csv.register_dialect(name, dialect)`](https://docs.python.org/fr/3/library/csv.html#csv.register_dialect).
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
class MonDialecte(csv.Dialect):
|
||||
"""Dialecte CSV utilisant des // comme séparateur."""
|
||||
delimiter = "//"
|
||||
|
||||
csv.register_dialect("mondialecte", MonDialecte)
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
#### Configuration via les attributs de Dialect
|
||||
|
||||
Pour gagner du temps, il est possible, lorsque vous créez votre objet lecteur ou écrivain, de passer des arguments pour
|
||||
préciser le format du fichier CSV à lire ou à écrire. Ces arguments ont les mêmes noms que les attributs de la classe `Dialect`, et ont le même usage.
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
with open("fichier.csv", "r", encoding="utf-8") as file:
|
||||
reader = csv.reader(file, delimiter=";", doublequotechar=True) # etc., etc.
|
||||
for row in reader:
|
||||
print(row)
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
### Détecter le dialecte d'un fichier
|
||||
|
||||
Outre la configuration de dialecte manuelle, il est possible de demander à Python d'autodétecter les propriétés de formatage d'un fichier CSV,
|
||||
et de générer automatiquement un objet `Dialect`. Pour cela, vous devez utiliser la classe `csv.Sniffer` :
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
with open("output.csv", "r", encoding="utf-8") as file:
|
||||
dialect = csv.Sniffer().sniff(file.read()) # Lire le contenu du fichier pour détecter le dialecte
|
||||
file.seek(0) # Revenir au début du fichier pour démarrer la lecture
|
||||
reader = csv.reader(file, dialect=dialect)
|
||||
for row in reader:
|
||||
print(row)
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
### Exemple : Copier un fichier CSV vers un fichier cible
|
||||
|
||||
On peut avec le module CSV lire un contenu CSV dans un objet Python, puis réécrire le contenu
|
||||
de cet objet dans un nouveau fichier CSV.
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
table = list() # Objet à utiliser pour stocker le contenu
|
||||
with open("fichier.csv", "r", encoding="utf-8") as file:
|
||||
reader = csv.reader(file, delimiter=",")
|
||||
for row in reader:
|
||||
table.append(row) # Ajouter la ligne comme élément de `table`
|
||||
|
||||
with open("copie.csv", "w", encoding="utf-8") as file:
|
||||
writer = csv.writer(file, delimiter=";")
|
||||
writer.writerows(table) # table doit être une liste de listes
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
### Lire chaque ligne sous forme de dictionnaire
|
||||
|
||||
Si votre fichier CSV contient des en-têtes, et que vous préférez les utiliser pour retrouver le
|
||||
contenu d'une colonne, plutôt qu'utiliser son indice, vous pouvez procéder
|
||||
à [l'utilisation d'un `DictReader`](https://docs.python.org/fr/3/library/csv.html#csv.DictReader) :
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
with open("example-file.csv", "r", encoding="utf-8") as csvfile:
|
||||
reader = csv.DictReader(csvfile) # lit automatiquement l'en-tête pour définir les clés des dictionnaires
|
||||
for row in reader: # lit les lignes suivantes, renvoyées sous forme de dictionnaires
|
||||
print(row)
|
||||
print(row["<texte de la colonne d'en-tête>"])
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
### Lire un CSV sans en-tête via dictionnaires
|
||||
|
||||
Si votre fichier CSV ne contient pas de ligne d'en-tête, mais que vous souhaitez quand même définir des en-têtes à
|
||||
associer aux colonnes, vous pouvez utiliser un `DictReader` et préciser un argument `fieldnames=` :
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
with open("example-file.csv", "r", encoding="utf-8") as csvfile:
|
||||
reader = csv.DictReader(csvfile, fieldnames=["col1", "col2"]) # définit les en-têtes
|
||||
for row in reader: # lit les lignes suivantes, renvoyées sous forme de dictionnaires
|
||||
print(row) # renvoie un dictionnaire avec des clés "col1" et "col2"
|
||||
print(row["col1"])
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
### Écrire un fichier CSV
|
||||
|
||||
```python {.numberLines}
|
||||
import csv
|
||||
|
||||
# Créer une liste de listes pour imiter une feuille de calcul
|
||||
rows = []
|
||||
for i in range(10):
|
||||
rows.append([str(j + i) for j in range(10)])
|
||||
|
||||
|
||||
# Sous Windows, il faut préciser un argument newline à la fonction open, sinon
|
||||
# chaque ajout de ligne causera deux passages à la ligne
|
||||
with open("output.csv", "w", encoding="utf-8", newline="") as file:
|
||||
writer = csv.writer(file, delimiter=";")
|
||||
writer.writerows(rows)
|
||||
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
### Écrire un CSV avec des en-têtes
|
||||
|
||||
```python {.numberLines}
|
||||
headers = ["nom", "prenom"]
|
||||
content = [
|
||||
{"nom": "Doe", "prenom": "John"},
|
||||
{"nom": "Smith", "prenom": "Jane"}
|
||||
]
|
||||
|
||||
with open("header-output.csv", "w", encoding="utf-8") as file:
|
||||
# Initialiser le writer en précisant la liste des textes d"'en-tête
|
||||
writer = csv.DictWriter(file, fieldnames=headers)
|
||||
# Écrire la ligne d'en-tête
|
||||
writer.writeheader()
|
||||
# Écrire le contenu restant
|
||||
writer.writerows(content)
|
||||
```
|
||||
|
||||
Exemple avec un `DictWriter`
|
Reference in New Issue
Block a user