1. Écriture avec écrasement
Lorsque vous travaillez avec des fichiers, « écrire dans un fichier » n’a pas toujours le même sens. Parfois, il faut complètement réécrire le fichier (par exemple, lorsque vous enregistrez un nouveau rapport), et parfois il faut ajouter de nouvelles informations à la fin d’un fichier existant (par exemple, tenir le journal des événements de l’application). Dans certains cas, il faut simplement lire le contenu du fichier sans le modifier.
En Java (notamment dans le package moderne java.nio.file), le mode d’accès au fichier est défini par un ensemble d’options spéciales que vous passez aux méthodes d’écriture, par exemple à Files.write(). Ces options permettent d’indiquer explicitement si vous souhaitez écraser le fichier ou ajouter quelque chose à la fin.
Comment ça marche
Lorsque vous appelez Files.write(path, data), Java crée par défaut un nouveau fichier ou écrase l’existant. Tout l’ancien contenu du fichier sera supprimé, et seules les nouvelles données resteront.
Ce comportement par défaut est en quelque sorte une « réinitialisation forcée » du fichier. S’il y avait auparavant 1000 lignes dans le fichier et que vous en écrivez une seule, tout ce qui existait avant disparaîtra sans laisser de trace.
Exemple : écrire des lignes dans un fichier
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class OverwriteFileExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
List<String> lines = List.of("Bonjour, monde !", "Ceci est une nouvelle ligne dans le fichier.");
// Nous écrivons les lignes dans le fichier (l’ancien contenu sera supprimé)
Files.write(path, lines);
System.out.println("Le fichier a été écrasé avec succès !");
}
}
Après l’exécution de ce code, il ne restera dans le fichier myfile.txt que les deux lignes de la liste lines. Tout ce qui existait auparavant disparaîtra (sans le moindre « remords » de la part de Java).
2. Ajout (append) : ajout de données en fin de fichier
Dans certaines tâches (par exemple, tenue d’un journal d’événements, logging, accumulation de données), il ne faut pas détruire l’ancien contenu du fichier, mais ajouter de nouvelles lignes à la fin. Dans l’ancien API, on utilisait souvent FileWriter avec le paramètre append = true. Avec l’API moderne, tout est plus simple et explicite.
Comment procéder
Il suffit de passer l’option StandardOpenOption.APPEND à la méthode Files.write() :
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
public class AppendFileExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
List<String> moreLines = List.of("Nous avons ajouté une ligne de plus.", "Et encore une !");
// Nous ajoutons les lignes à la fin du fichier (l’ancien contenu est conservé)
Files.write(path, moreLines, StandardOpenOption.APPEND);
System.out.println("Les lignes ont été ajoutées à la fin du fichier !");
}
}
Point important : si le fichier n’existe pas, une erreur se produira lors de la tentative d’ajout — Java ne créera pas le fichier automatiquement en mode APPEND. Pour créer le fichier s’il n’existe pas, utilisez deux options à la fois :
Files.write(path, moreLines, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
À quoi cela ressemble en pratique
- Vous lancez le programme avec écriture (réécriture) — le fichier contient deux lignes.
- Vous lancez le programme en mode ajout — de nouvelles lignes s’ajoutent aux existantes, l’ancien contenu reste en place.
3. Combinaison des options : CREATE, APPEND, TRUNCATE_EXISTING
En Java, on peut combiner plusieurs options pour un contrôle plus fin des modes d’ouverture du fichier :
- StandardOpenOption.CREATE — créer le fichier s’il n’existe pas.
- StandardOpenOption.APPEND — ajouter des données à la fin.
- StandardOpenOption.TRUNCATE_EXISTING — tronquer le fichier à zéro octet (vider) s’il existe.
- StandardOpenOption.CREATE_NEW — créer un nouveau fichier ; s’il existe déjà — erreur.
Exemple : créer le fichier s’il n’existe pas et ajouter des données à la fin
Files.write(path, moreLines, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
Exemple : créer le fichier ou le vider complètement puis écrire de nouvelles données
Files.write(path, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
Tableau : principaux modes d’écriture
| Option(s) | Comportement |
|---|---|
| (par défaut) | Créer le fichier ou écraser l’existant |
|
Ajouter à la fin, erreur si le fichier n’existe pas |
|
Ajouter à la fin, créer le fichier s’il n’existe pas |
|
Tronquer le fichier à zéro et écrire de nouvelles données |
|
Créer un nouveau fichier, erreur si le fichier existe déjà |
4. Lecture et écriture de fichiers binaires
Jusqu’à présent, nous avons travaillé avec des chaînes. Mais il faut parfois écrire ou lire des octets « bruts » (par exemple, des images, des archives, des fichiers PDF). Dans ce cas, utilisez :
- Files.readAllBytes(path) — lit le fichier dans un tableau d’octets.
- Files.write(path, byteArray) — écrit un tableau d’octets dans un fichier.
Exemple : copie d’un fichier
import java.nio.file.*;
import java.io.IOException;
public class CopyBinaryFileExample {
public static void main(String[] args) throws IOException {
Path source = Paths.get("logo.png");
Path target = Paths.get("logo_copy.png");
byte[] data = Files.readAllBytes(source);
Files.write(target, data);
System.out.println("Fichier copié !");
}
}
Exemple : ajout de données binaires
byte[] moreData = new byte[] {1, 2, 3, 4, 5};
Files.write(path, moreData, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
Attention : ajouter des données binaires à un fichier qui contient déjà des données structurées (par exemple, une image) l’endommagera généralement. Utilisez l’ajout uniquement pour des fichiers texte ou des fichiers binaires spécialement conçus pour cela (par exemple, des logs).
5. Quand utiliser les flux (FileInputStream, BufferedReader, etc.)
Les méthodes Files.readAllBytes(), Files.write() sont pratiques pour des fichiers petits à moyens, lorsque l’on peut tranquillement charger tout le contenu en mémoire en une seule fois. Si le fichier est volumineux (par exemple, des gigaoctets), ou si vous souhaitez le lire ligne par ligne (par exemple, pour analyser des logs), utilisez des flux.
Exemple : lecture d’un fichier ligne par ligne avec BufferedReader
import java.nio.file.*;
import java.io.*;
public class ReadLinesExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Ligne : " + line);
}
}
}
}
Exemple : écriture ligne par ligne avec BufferedWriter et ajout
import java.nio.file.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class WriteAppendExample {
public static void main(String[] args) throws IOException {
Path path = Paths.get("myfile.txt");
try (BufferedWriter writer = Files.newBufferedWriter(
path,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.APPEND)) {
writer.write("Une ligne de plus via BufferedWriter !");
writer.newLine();
}
}
}
6. Pratique : créer un fichier et y ajouter des lignes
Mettons tout ensemble dans une application. Supposons que nous ayons un programme qui maintient une liste de tâches (todo‑list) dans un fichier texte. Chaque fois que l’utilisateur ajoute une nouvelle tâche, nous l’ajoutons à la fin du fichier.
import java.nio.file.*;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
public class TodoList {
public static void main(String[] args) throws IOException {
Path path = Paths.get("todo.txt");
Scanner scanner = new Scanner(System.in);
System.out.print("Saisissez une nouvelle tâche : ");
String task = scanner.nextLine();
// Nous ajoutons la tâche à la fin du fichier (créons le fichier s’il n’existe pas)
Files.write(path,
List.of(task),
StandardOpenOption.CREATE,
StandardOpenOption.APPEND);
System.out.println("Tâche ajoutée !");
}
}
Exécutez le programme plusieurs fois — chaque tâche apparaîtra dans le fichier sur une nouvelle ligne. Et voilà notre première todo‑list « persistante » !
7. Gestion des erreurs : que peut-il se passer ?
Le travail avec des fichiers s’accompagne toujours d’un risque d’erreurs. Voici ce qui peut arriver :
- IOException — l’exception de base pour toutes les erreurs d’entrée/sortie. Peut survenir si le fichier est utilisé par un autre programme, en cas d’absence de droits de lecture/écriture, si le disque est plein, etc.
- NoSuchFileException — si vous essayez de lire ou d’ajouter à un fichier qui n’existe pas (et que vous n’avez pas indiqué l’option CREATE).
- FileAlreadyExistsException — si vous utilisez l’option CREATE_NEW alors que le fichier existe déjà.
Recommandation : encapsulez toujours le travail avec les fichiers dans un try-catch :
try {
// vos opérations avec les fichiers
} catch (IOException e) {
System.out.println("Erreur lors du traitement du fichier : " + e.getMessage());
}
8. Erreurs fréquentes avec les modes d’ouverture de fichiers
Erreur n° 1 : vous avez oublié d’indiquer l’option CREATE lors d’un ajout. Si vous essayez d’ajouter (APPEND) à un fichier qui n’existe pas encore, vous obtiendrez une NoSuchFileException. Ajoutez toujours CREATE si vous voulez que le fichier soit créé automatiquement.
Erreur n° 2 : écrasement accidentel d’un fichier. Appeler Files.write(path, data) sans options supplémentaires détruit tout l’ancien contenu du fichier. Si vous voulez ajouter des données plutôt que supprimer l’existant, utilisez APPEND.
Erreur n° 3 : tentative d’ajouter des données binaires dans un fichier texte (ou l’inverse). Si vous mélangez données texte et binaires dans un même fichier, vous obtiendrez très probablement un fichier illisible. Respectez toujours un format unique par fichier.
Erreur n° 4 : oubli de fermer le flux. Si vous utilisez des flux (BufferedWriter, BufferedReader), n’oubliez pas de les fermer (de préférence via try-with-resources), sinon le fichier peut rester verrouillé.
Erreur n° 5 : absence de gestion des exceptions. Toute opération avec des fichiers peut lever une IOException. Si l’erreur n’est pas gérée, le programme se terminera brutalement.
GO TO FULL VERSION