Comment lire un fichier dans une chaîne Java : analyse d'une question populaire de Stack Overflow
\n\nSi vous vous êtes déjà demandé « How do I create a Java string from the contents of a file? » — vous n'êtes pas seul. Cette question a recueilli des milliers de vues sur Stack Overflow, et pour cause. Une opération apparemment banale — lire un fichier texte et placer son contenu dans une chaîne — cache de nombreux pièges : des fuites mémoire aux problèmes d'encodage.
\n\nDans cet article, nous ne nous contenterons pas de donner des recettes toutes faites. Nous allons regarder sous le capot de chaque méthode : comment fonctionne BufferedReader, pourquoi Files.readString() n'est apparu qu'en Java 11, et en quoi Scanner diffère de InputStream. C'est parti !
Pourquoi est-ce plus complexe qu'il n'y paraît ?
\n\nÀ première vue, la tâche est triviale : il y a un fichier, il y a une chaîne. Mais Java est un langage à typage strict et à mémoire gérée. Pour lire un fichier, il faut :
\n\n- \n
- Ouvrir un flux de données (InputStream, Reader) \n
- Bufferiser la lecture (pour ne pas solliciter le disque à chaque octet) \n
- Assembler les octets/caractères dans un tampon unique \n
- Convertir en chaîne en tenant compte de l'encodage \n
- Fermer les ressources (pour éviter les fuites de descripteurs de fichiers) \n
Chacune de ces étapes est un point de défaillance potentiel. Analysons les approches les plus populaires.
\n\nMéthode 1 : La classique — BufferedReader (Java 1.1+)
\n\nLa méthode la plus ancienne et la plus éprouvée. Elle utilise BufferedReader pour une lecture ligne par ligne. Sous le capot, un tampon (8 Ko par défaut) accumule les données, réduisant le nombre d'appels système read().
public static String readFileBufferedReader(String filePath) throws IOException {\n StringBuilder content = new StringBuilder();\n try (BufferedReader reader = new BufferedReader(new FileReader(filePath, StandardCharsets.UTF_8))) {\n String line;\n while ((line = reader.readLine()) != null) {\n content.append(line).append(System.lineSeparator());\n }\n }\n return content.toString();\n}\n\nComment cela fonctionne-t-il sous le capot ?
\nFileReader ouvre le fichier et crée un FileInputStream. BufferedReader l'encapsule en ajoutant un tampon. Lors de l'appel à readLine(), un bloc de données (par exemple, 8 Ko) est lu, et la ligne est assemblée à partir du tampon. Si la ligne est plus longue que le tampon, celui-ci est relu. try-with-resources garantit la fermeture du flux même en cas d'exception.
Avantages : Fonctionne sur n'importe quelle version de Java, lecture ligne par ligne contrôlée.
\nInconvénients : Ajoute un séparateur de ligne supplémentaire à la fin, inefficace pour les très gros fichiers (chaîne en mémoire).
Méthode 2 : Scanner — pratique mais lent (Java 5+)
\n\nBeaucoup aiment Scanner pour sa simplicité. Il sait diviser l'entrée en tokens, mais pour lire un fichier entier, on l'utilise ainsi :
public static String readFileScanner(String filePath) throws IOException {\n try (Scanner scanner = new Scanner(new File(filePath), StandardCharsets.UTF_8.name())) {\n scanner.useDelimiter("\\\\A"); // Début du fichier comme délimiteur\n return scanner.hasNext() ? scanner.next() : "";\n }\n}\n\nLa magie de \\\\A : C'est une expression régulière désignant le début des données d'entrée. En la définissant comme délimiteur, on force Scanner à lire tout le contenu en une seule fois. Sous le capot, Scanner utilise Pattern pour rechercher le délimiteur, ce qui ajoute des frais généraux liés aux expressions régulières.
Avantages : Code très concis.
\nInconvénients : Plus lent que BufferedReader à cause du travail avec les regex, ne convient pas aux fichiers binaires.
Méthode 3 : Files.readAllLines() — la commodité de Java 8 (NIO)
\n\nDepuis Java 8, le paquet java.nio.file a proposé une solution élégante