Lecture et écriture de fichiers CSV¶
Aperçu¶
Les fichiers CSV (Comma-Separated Values) sont l’un des formats les plus courants pour stocker des données tabulaires.
Le module intégré csv de Python fournit des outils puissants pour lire et écrire des fichiers CSV tout en gérant
automatiquement de nombreux cas particuliers.
Pourquoi utiliser le module csv ?¶
Bien que vous puissiez lire des fichiers CSV avec des opérations de base sur les fichiers, le module csv gère :
- Les champs cités contenant des virgules
- Les guillemets échappés dans les champs
- Différents délimiteurs (virgules, points-virgules, tabulations)
- Les sauts de ligne dans les champs cités
- Divers dialectes et formats CSV
Lecture de base des fichiers CSV¶
Méthode 1 : Lecture sous forme de listes¶
import csv
# Lire un fichier CSV ligne par ligne
with open('data.csv', 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.reader(file)
# Lire la ligne d'en-tête
header = next(csv_reader)
print("En-tête:", header)
# Lire les lignes de données
for row in csv_reader:
print(row)
Méthode 2 : Lecture sous forme de dictionnaires¶
import csv
# Lire un CSV avec les noms de colonnes comme clés de dictionnaire
with open('data.csv', 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
print(f"Nom: {row['name']}, Âge: {row['age']}, Ville: {row['city']}")
Écriture de base des fichiers CSV¶
Méthode 1 : Écriture de listes¶
import csv
# Écrire des données sous forme de listes
data = [
['Nom', 'Âge', 'Ville'],
['Alice', '25', 'New York'],
['Bob', '30', 'San Francisco'],
['Charlie', '35', 'Chicago']
]
with open('output.csv', 'w', encoding='utf-8', newline='') as file:
csv_writer = csv.writer(file)
for row in data:
csv_writer.writerow(row)
# Ou écrire toutes les lignes en une fois
# csv_writer.writerows(data)
Méthode 2 : Écriture de dictionnaires¶
import csv
# Définir des données sous forme de dictionnaires
data = [
{'name': 'Alice', 'age': 25, 'city': 'New York'},
{'name': 'Bob', 'age': 30, 'city': 'San Francisco'},
{'name': 'Charlie', 'age': 35, 'city': 'Chicago'}
]
fieldnames = ['name', 'age', 'city']
with open('output.csv', 'w', encoding='utf-8', newline='') as file:
csv_writer = csv.DictWriter(file, fieldnames=fieldnames)
# Écrire l'en-tête
csv_writer.writeheader()
# Écrire les lignes de données
for row in data:
csv_writer.writerow(row)
# Ou écrire toutes les lignes en une fois
# csv_writer.writerows(data)
Gestion des différents formats CSV¶
Délimiteurs personnalisés¶
import csv
# Lecture d'un fichier séparé par des points-virgules
with open('data.csv', 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.reader(file, delimiter=';')
for row in csv_reader:
print(row)
# Écriture avec un délimiteur personnalisé
with open('output.csv', 'w', encoding='utf-8', newline='') as file:
csv_writer = csv.writer(file, delimiter=';')
csv_writer.writerow(['Nom', 'Âge', 'Ville'])
csv_writer.writerow(['Alice', '25', 'New York'])
Valeurs séparées par des tabulations (TSV)¶
import csv
# Lecture de fichiers TSV
with open('data.tsv', 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.reader(file, delimiter='\t')
for row in csv_reader:
print(row)
# Écriture de fichiers TSV
with open('output.tsv', 'w', encoding='utf-8', newline='') as file:
csv_writer = csv.writer(file, delimiter='\t')
csv_writer.writerow(['Nom', 'Âge', 'Ville'])
Gestion des guillemets et caractères spéciaux¶
import csv
# Données avec virgules et guillemets
data = [
['Nom', 'Description', 'Prix'],
['Apple iPhone', 'Téléphone "flagship" le plus récent, très cher', '$999'],
['Samsung Galaxy', 'Téléphone Android, bon rapport qualité-prix', '$699'],
['Google Pixel', 'Expérience Android pure, appareil photo excellent', '$799']
]
# Écrire avec une citation appropriée
with open('products.csv', 'w', encoding='utf-8', newline='') as file:
csv_writer = csv.writer(file, quoting=csv.QUOTE_ALL) # Citer tous les champs
csv_writer.writerows(data)
# Différentes options de citation :
# csv.QUOTE_ALL - Citer tous les champs
# csv.QUOTE_MINIMAL - Citer uniquement lorsque nécessaire (par défaut)
# csv.QUOTE_NONNUMERIC - Citer tous les champs non numériques
# csv.QUOTE_NONE - Ne jamais citer (à utiliser avec prudence)
Exemples pratiques¶
Exemple 1 : Gestionnaire de notes d’élèves¶
import csv
def read_grades(filename):
"""Lire les notes des élèves depuis un fichier CSV"""
students = []
try:
with open(filename, 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
# Convertir les notes en entiers
student = {
'name': row['name'],
'math': int(row['math']),
'science': int(row['science']),
'english': int(row['english'])
}
students.append(student)
except FileNotFoundError:
print(f"Fichier {filename} introuvable")
return []
except ValueError as e:
print(f"Erreur de conversion des notes en nombres: {e}")
return []
return students
def calculate_averages(students):
"""Calculer la moyenne pour chaque élève"""
for student in students:
avg = (student['math'] + student['science'] + student['english']) / 3
student['average'] = round(avg, 2)
return students
def write_grades_with_averages(students, filename):
"""Écrire les élèves et leurs moyennes dans un CSV"""
fieldnames = ['name', 'math', 'science', 'english', 'average']
with open(filename, 'w', encoding='utf-8', newline='') as file:
csv_writer = csv.DictWriter(file, fieldnames=fieldnames)
csv_writer.writeheader()
csv_writer.writerows(students)
# Utilisation
students = read_grades('grades.csv')
students_with_avg = calculate_averages(students)
write_grades_with_averages(students_with_avg, 'grades_with_averages.csv')
print(f"Traitement de {len(students)} élèves")
Exemple 2 : Analyse des données de vente¶
import csv
from datetime import datetime
def read_sales_data(filename):
"""Lire les données de vente et convertir les types de données"""
sales = []
with open(filename, 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
sale = {
'date': datetime.strptime(row['date'], '%Y-%m-%d'),
'product': row['product'],
'quantity': int(row['quantity']),
'price': float(row['price']),
'salesperson': row['salesperson']
}
sale['total'] = sale['quantity'] * sale['price']
sales.append(sale)
return sales
def generate_sales_report(sales, output_filename):
"""Générer un rapport de vente"""
# Calculer les totaux par vendeur
salesperson_totals = {}
for sale in sales:
person = sale['salesperson']
if person not in salesperson_totals:
salesperson_totals[person] = {'sales': 0, 'revenue': 0.0}
salesperson_totals[person]['sales'] += sale['quantity']
salesperson_totals[person]['revenue'] += sale['total']
# Écrire le rapport
with open(output_filename, 'w', encoding='utf-8', newline='') as file:
fieldnames = ['salesperson', 'total_sales', 'total_revenue', 'avg_per_sale']
csv_writer = csv.DictWriter(file, fieldnames=fieldnames)
csv_writer.writeheader()
for person, data in salesperson_totals.items():
avg_per_sale = data['revenue'] / data['sales'] if data['sales'] > 0 else 0
csv_writer.writerow({
'salesperson': person,
'total_sales': data['sales'],
'total_revenue': round(data['revenue'], 2),
'avg_per_sale': round(avg_per_sale, 2)
})
# Utilisation
sales_data = read_sales_data('sales.csv')
generate_sales_report(sales_data, 'sales_report.csv')
Exemple 3 : Nettoyage et validation des données¶
import csv
import re
def validate_email(email):
"""Validation simple de l'email"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
def clean_phone_number(phone):
"""Nettoyer et formater le numéro de téléphone"""
# Supprimer tous les caractères non numériques
digits = re.sub(r'\D', '', phone)
# Formater comme (XXX) XXX-XXXX si 10 chiffres
if len(digits) == 10:
return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}"
elif len(digits) == 11 and digits[0] == '1':
return f"({digits[1:4]}) {digits[4:7]}-{digits[7:]}"
else:
return phone # Retourner l'original si impossible à formater
def clean_customer_data(input_filename, output_filename, error_filename):
"""Nettoyer les données clients et séparer les erreurs"""
clean_data = []
error_data = []
with open(input_filename, 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.DictReader(file)
for row_num, row in enumerate(csv_reader, start=2): # Commencer à 2 pour l'en-tête
errors = []
# Nettoyer et valider les données
name = row['name'].strip().title()
email = row['email'].strip().lower()
phone = clean_phone_number(row['phone'])
# Valider
if not name:
errors.append("Nom manquant")
if not validate_email(email):
errors.append("Email invalide")
if not phone or len(re.sub(r'\D', '', phone)) < 10:
errors.append("Téléphone invalide")
# Préparer la ligne nettoyée
cleaned_row = {
'name': name,
'email': email,
'phone': phone
}
if errors:
cleaned_row['errors'] = '; '.join(errors)
cleaned_row['row_number'] = row_num
error_data.append(cleaned_row)
else:
clean_data.append(cleaned_row)
# Écrire les données nettoyées
if clean_data:
with open(output_filename, 'w', encoding='utf-8', newline='') as file:
fieldnames = ['name', 'email', 'phone']
csv_writer = csv.DictWriter(file, fieldnames=fieldnames)
csv_writer.writeheader()
csv_writer.writerows(clean_data)
# Écrire les données d'erreur
if error_data:
with open(error_filename, 'w', encoding='utf-8', newline='') as file:
fieldnames = ['row_number', 'name', 'email', 'phone', 'errors']
csv_writer = csv.DictWriter(file, fieldnames=fieldnames)
csv_writer.writeheader()
csv_writer.writerows(error_data)
return len(clean_data), len(error_data)
# Utilisation
clean_count, error_count = clean_customer_data(
'customers_raw.csv',
'customers_clean.csv',
'customers_errors.csv'
)
print(f"{clean_count} enregistrements nettoyés, {error_count} erreurs trouvées")
Fonctionnalités avancées¶
Travail avec de grands fichiers CSV¶
import csv
def process_large_csv(filename, batch_size=1000):
"""Traiter les grands fichiers CSV par lots"""
with open(filename, 'r', encoding='utf-8', newline='') as file:
csv_reader = csv.DictReader(file)
batch = []
for row in csv_reader:
batch.append(row)
if len(batch) >= batch_size:
# Traiter le lot
process_batch(batch)
batch = []
# Traiter les lignes restantes
if batch:
process_batch(batch)
def process_batch(batch):
"""Traiter un lot de lignes"""
print(f"Traitement d'un lot de {len(batch)} lignes")
# Votre logique de traitement ici
Dialectes CSV personnalisés¶
import csv
# Définir un dialecte CSV personnalisé
csv.register_dialect('custom',
delimiter='|',
quotechar='"',
quoting=csv.QUOTE_MINIMAL,
lineterminator='\n')
# Utiliser le dialecte personnalisé
with open('data.csv', 'w', encoding='utf-8', newline='') as file:
csv_writer = csv.writer(file, dialect='custom')
csv_writer.writerow(['Nom', 'Âge', 'Ville'])
csv_writer.writerow(['Alice', '25', 'New York'])
Bonnes pratiques de gestion des erreurs¶
import csv
def robust_csv_reader(filename):
"""Lire un CSV avec une gestion d'erreur complète"""
try:
with open(filename, 'r', encoding='utf-8', newline='') as file:
# Essayer de détecter le dialecte
sample = file.read(1024)
file.seek(0)
try:
dialect = csv.Sniffer().sniff(sample)
csv_reader = csv.reader(file, dialect)
except csv.Error:
# Revenir au défaut
csv_reader = csv.reader(file)
data = []
for row_num, row in enumerate(csv_reader, start=1):
try:
# Traiter la ligne
data.append(row)
except Exception as e:
print(f"Erreur lors du traitement de la ligne {row_num}: {e}")
continue
return data
except FileNotFoundError:
print(f"Fichier {filename} introuvable")
return []
except PermissionError:
print(f"Permission refusée pour accéder à {filename}")
return []
except UnicodeDecodeError as e:
print(f"Erreur d'encodage: {e}")
return []
except csv.Error as e:
print(f"Erreur de parsing CSV: {e}")
return []
# Utilisation
data = robust_csv_reader('data.csv')
Points clés à retenir¶
- Utilisez toujours
newline=''lors de l’ouverture des fichiers CSV pour éviter les lignes vides supplémentaires sous Windows - Spécifiez l’encodage (de préférence UTF-8) pour une comportement cohérent entre les plateformes
- Utilisez
DictReaderetDictWriterpour un code plus lisible lorsque vous travaillez avec des en-têtes - Gérez les conversions de types de données explicitement (le module CSV lit tout comme des chaînes)
- Validez et nettoyez les données lors de leur lecture
- Utilisez une citation appropriée lors de l’écriture des fichiers CSV avec des caractères spéciaux
- Gérez les erreurs avec grâce à l’aide de blocs try-except
- Considérez l’utilisation de la mémoire lorsque vous travaillez avec de grands fichiers
Erreurs courantes à éviter¶
- Ne pas utiliser le paramètre
newline=''(cause des lignes vides supplémentaires) - Oublier que le module CSV lit tout comme des chaînes
- Ne pas gérer correctement les virgules, guillemets ou sauts de ligne dans les données
- Supposer que tous les fichiers CSV utilisent des délimiteurs par virgule
- Ne pas valider les types de données lors de la lecture
- Oublier d’écrire les en-têtes lorsque vous utilisez
DictWriter - Ne pas gérer correctement les problèmes d’encodage
Ce guide fournit une base solide pour travailler avec les fichiers CSV en Python, gérant les scénarios et cas particuliers les plus courants que vous rencontrerez dans des applications réelles.