Fichiers et flux
Cours · programmation Java (école d'ingénieur), chapitre 14 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
Jusqu'ici, nos programmes oubliaient tout en s'arrêtant : les données venaient du clavier ou étaient écrites dans le code, les résultats partaient à l'écran. Un vrai logiciel, lui, garde ses données : un tableur enregistre ses feuilles, un jeu sauvegarde la partie, un serveur écrit ses journaux. Ce chapitre apprend à lire et à écrire des fichiers en Java : des fichiers texte d'abord (le cas le plus fréquent), puis des fichiers binaires.
À faire sur votre ordinateur
Les programmes de ce chapitre lisent et écrivent sur le disque. L'Éditeur Java de la plateforme, qui tourne dans le navigateur, n'a pas accès au système de fichiers : il faut un JDK installé sur votre ordinateur, et exécuter les programmes dans un terminal (ou dans un environnement de développement). Les fichiers sont lus et créés dans le dossier courant, celui depuis lequel on lance java.
Objectifs du chapitre
- Désigner un fichier par un chemin (
Path), relatif ou absolu. - Lire et écrire un fichier texte en une instruction avec la classe
Files. - Lire un gros fichier ligne par ligne avec un
BufferedReader, écrire avec unPrintWriter. - Comprendre la différence entre caractères et octets, et le rôle de l'encodage (UTF-8).
- Fermer systématiquement ses fichiers avec
try-avec-ressources et traiter lesIOException. - Écrire et relire des données binaires.
14.1 Fichiers et chemins
14.1.1 Qu'est-ce qu'un fichier ?
Un fichier est une suite d'octets enregistrée sur un support de stockage (disque, clé USB…) et désignée par un nom. Les fichiers sont rangés dans des dossiers (ou répertoires), eux-mêmes rangés dans des dossiers : ils forment une arborescence. Un chemin désigne un fichier ou un dossier dans cette arborescence, par la suite des noms des dossiers à traverser.
Un chemin peut être :
- absolu : il part de la racine du disque, par exemple
/home/eleve/projet/notes.csvsous Linux et macOS, ouC:\Users\eleve\projet\notes.csvsous Windows ; - relatif : il part du dossier courant du programme, par exemple
donnees/notes.csv. Le dossier courant est celui depuis lequel on a lancé la commandejava.
Deux noms sont réservés : . désigne le dossier lui-même et .. le dossier parent.
Attention
Un programme qui ouvre notes.csv cherche ce fichier dans le dossier courant, pas dans le dossier où se trouve le fichier .java ou .class. C'est la première cause de l'erreur « fichier introuvable » chez les débutants : vérifiez d'où vous lancez le programme.
14.1.2 La classe Path
Depuis Java 7, un chemin est représenté par un objet de type java.nio.file.Path, que l'on fabrique avec Path.of(...) (ou, dans du code plus ancien, Paths.get(...), qui fait la même chose). On lui donne les noms successifs, et Java insère lui-même le bon séparateur (/ ou \) selon le système.
import java.nio.file.Path;
/** Chapitre 14 — manipuler des chemins (sans toucher au disque). */
public class Chemins {
public static void main(String[] args) {
Path p = Path.of("donnees", "notes.csv"); // chemin relatif
System.out.println("Chemin : " + p);
System.out.println("Nom du fichier : " + p.getFileName());
System.out.println("Dossier parent : " + p.getParent());
System.out.println("Absolu ? : " + p.isAbsolute());
System.out.println("Nombre de noms : " + p.getNameCount());
Path q = Path.of("/home/eleve/projet"); // chemin absolu (Unix)
System.out.println("Absolu ? : " + q.isAbsolute());
System.out.println("Résolution : " + q.resolve(p));
System.out.println("Normalisation : " + Path.of("a/b/../c/./d.txt").normalize());
System.out.println("Relatif : " + q.relativize(Path.of("/home/eleve/cours/java")));
// Un chemin relatif se lit à partir du dossier courant du programme :
Path absolu = p.toAbsolutePath();
System.out.println("Se termine par p : " + absolu.endsWith(p));
}
}
Chemin : donnees/notes.csv
Nom du fichier : notes.csv
Dossier parent : donnees
Absolu ? : false
Nombre de noms : 2
Absolu ? : true
Résolution : /home/eleve/projet/donnees/notes.csv
Normalisation : a/c/d.txt
Relatif : ../cours/java
Se termine par p : true
Un Path n'est qu'un nom : le créer ne crée aucun fichier et ne vérifie pas qu'il existe. resolve colle un chemin relatif au bout d'un autre, normalize élimine les . et les .., relativize calcule le chemin pour aller de l'un à l'autre, et toAbsolutePath complète un chemin relatif par le dossier courant.
14.2 Lire et écrire en une instruction : la classe Files
La classe java.nio.file.Files rassemble des méthodes static pour agir sur les fichiers. Les plus utiles :
| Méthode | Effet |
|---|---|
Files.exists(p) | le fichier ou le dossier existe-t-il ? |
Files.size(p) | taille du fichier, en octets |
Files.readString(p) | tout le contenu, dans une String |
Files.readAllLines(p) | toutes les lignes, dans une List |
Files.writeString(p, s) | écrit la chaîne s |
Files.write(p, lignes) | écrit une liste de lignes |
Files.lines(p) | un flux (Stream) des lignes |
Files.delete(p) | supprime le fichier |
Files.createDirectories(p) | crée un dossier (et ses parents) |
Par défaut, ces méthodes lisent et écrivent en UTF-8 (section 14.3), et les méthodes d'écriture écrasent le fichier s'il existe déjà. Pour écrire à la suite du contenu existant, on ajoute en dernier argument l'option StandardOpenOption.APPEND.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
/** Chapitre 14 — écrire puis relire un fichier texte avec la classe Files. */
public class LireEcrire {
public static void main(String[] args) throws IOException {
Path f = Path.of("courses.txt");
// 1) Écrire une liste de lignes (le fichier est créé ou écrasé)
List<String> lignes = List.of("pain", "lait", "œufs", "café");
Files.write(f, lignes);
System.out.println("Existe : " + Files.exists(f) + ", taille : " + Files.size(f) + " octets");
// 2) Ajouter une ligne à la fin
Files.writeString(f, "pâtes\n", java.nio.file.StandardOpenOption.APPEND);
// 3) Relire tout le fichier, ligne par ligne
List<String> relues = Files.readAllLines(f);
for (int i = 0; i < relues.size(); i++)
System.out.println((i + 1) + " : " + relues.get(i));
// 4) Relire tout le fichier d'un bloc
String tout = Files.readString(f);
System.out.println("Caractères : " + tout.length());
}
}
Existe : true, taille : 22 octets
1 : pain
2 : lait
3 : œufs
4 : café
Caractères : 26
Remarquez deux choses. D'abord Files.write termine chaque ligne par un saut de ligne : les quatre lignes occupent octets, et non . Ensuite le fichier final contient caractères mais octets : « œ », « é » et « â » occupent deux octets chacun en UTF-8. Nous y reviendrons.
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.