1653 votes

Comment créer une chaîne Java à partir du contenu d'un fichier ?

J'utilise l'idiome ci-dessous depuis un certain temps déjà. Et il semble être le plus répandu, du moins sur les sites que j'ai visités.

Existe-t-il une meilleure/différente façon de lire un fichier dans une chaîne de caractères en Java ?

private String readFile(String file) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader (file));
    String         line = null;
    StringBuilder  stringBuilder = new StringBuilder();
    String         ls = System.getProperty("line.separator");

    try {
        while((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }

        return stringBuilder.toString();
    } finally {
        reader.close();
    }
}

7 votes

Quelqu'un peut-il m'expliquer de manière très simple ce qu'est le NIO ? Chaque fois que je lis quelque chose à ce sujet, je me perds dans la énième mention du canal :(

7 votes

N'oubliez pas qu'il n'est pas garanti que le séparateur de ligne du fichier ne soit pas nécessairement le même que le séparateur de ligne du système.

6 votes

Le code ci-dessus a un bug qui consiste à ajouter un caractère supplémentaire à la dernière ligne. Il devrait être quelque chose comme suit if(line = reader.readLine() ) != null){ stringBuilder.append( line ) ; } while (line = reader.readLine() ) != null) { stringBuilder.append( ls ) ; stringBuilder.append( line ) ; }

1716voto

erickson Points 127945

Lire tout le texte d'un fichier

Java 11 a ajouté le readString() pour lire les petits fichiers en tant que String en préservant les terminaisons de ligne :

String content = Files.readString(path, StandardCharsets.US_ASCII);

Pour les versions entre Java 7 et 11, voici un idiome compact et robuste, emballé dans une méthode utilitaire :

static String readFile(String path, Charset encoding)
  throws IOException
{
  byte[] encoded = Files.readAllBytes(Paths.get(path));
  return new String(encoded, encoding);
}

Lire les lignes de texte d'un fichier

Java 7 a ajouté un Méthode pratique pour lire un fichier sous forme de lignes de texte, représenté par un List<String> . Cette approche est "avec perte" car les séparateurs de ligne sont supprimés à la fin de chaque ligne.

List<String> lines = Files.readAllLines(Paths.get(path), encoding);

Java 8 a ajouté le Files.lines() pour produire un Stream<String> . Là encore, cette méthode entraîne des pertes car les séparateurs de ligne sont supprimés. Si un IOException est rencontrée lors de la lecture du fichier, elle est enveloppée dans un fichier de type UncheckedIOException puisque Stream n'accepte pas les lambdas qui lèvent des exceptions vérifiées.

try (Stream<String> lines = Files.lines(path, encoding)) {
  lines.forEach(System.out::println);
}

Ce site Stream a besoin d'un close() ce qui est mal documenté dans l'API, et je pense que beaucoup de personnes ne le remarquent même pas. Stream a un close() méthode. Veillez à utiliser un bloc ARM comme indiqué.

Si vous travaillez avec une source autre qu'un fichier, vous pouvez utiliser la fonction lines() méthode dans BufferedReader à la place.

Utilisation de la mémoire

La première méthode, qui préserve les sauts de ligne, peut temporairement nécessiter une mémoire plusieurs fois supérieure à la taille du fichier, car pendant un court instant, le contenu brut du fichier (un tableau d'octets) et les caractères décodés (chacun d'entre eux comporte 16 bits même s'ils sont codés sur 8 bits dans le fichier) résident en mémoire en même temps. Il est plus sûr de s'appliquer à des fichiers que vous savez être petits par rapport à la mémoire disponible.

La deuxième méthode, la lecture des lignes, est généralement plus efficace en termes de mémoire, car le tampon d'octets d'entrée pour le décodage n'a pas besoin de contenir le fichier entier. Cependant, elle n'est toujours pas adaptée aux fichiers qui sont très volumineux par rapport à la mémoire disponible.

Pour lire des fichiers volumineux, vous devez concevoir un programme différent, qui lit un morceau de texte dans un flux, le traite, puis passe au suivant, en réutilisant le même bloc de mémoire de taille fixe. Ici, le terme "gros" dépend des spécifications de l'ordinateur. De nos jours, ce seuil peut correspondre à plusieurs gigaoctets de RAM. La troisième méthode, qui utilise un Stream<String> est une façon de le faire, si vos "enregistrements" d'entrée sont des lignes individuelles. (En utilisant la fonction readLine() méthode de BufferedReader est l'équivalent procédural de cette approche).

Codage des caractères

Une chose qui manque dans l'échantillon de l'article original est le codage des caractères. Il existe des cas particuliers où la plateforme par défaut est ce que vous voulez, mais ils sont rares, et vous devriez pouvoir justifier votre choix.

El StandardCharsets La classe définit certaines constantes pour les codages requis par tous les moteurs d'exécution Java :

String content = readFile("test.txt", StandardCharsets.UTF_8);

La plateforme par défaut est disponible à partir de le site Charset classe lui-même :

String content = readFile("test.txt", Charset.defaultCharset());

Note : Cette réponse remplace en grande partie ma version de Java 6. L'utilité de Java 7 simplifie considérablement le code et l'ancienne réponse, qui utilisait un tampon d'octets mappé, empêchait la suppression du fichier lu jusqu'à ce que le tampon mappé soit vidé. Vous pouvez voir l'ancienne version via le lien "edited" sur cette réponse.

0 votes

Très intéressant. Que signifie le canal. Je sais que c'est pour éviter de bloquer le "fil" ? Ils peuvent être bidirectionnels (ou c'est ce que j'ai compris) mais, en termes plus simples, que sont-ils ? Pouvez-vous nous en dire plus ?

0 votes

À bien des égards, un ReadableByteChannel ressemble à un InputStream, et un WritableByteChannel à un OutputStream. De nombreux canaux concrets mettent en œuvre ces deux interfaces, de sorte qu'un objet est bidirectionnel. Certains canaux (SocketChannel) prennent en charge les E/S non bloquantes, mais ce n'est pas le cas de tous les canaux.

0 votes

Connaissez-vous l'efficacité de cet idiome en termes de temps et de mémoire, ou pouvez-vous au moins l'estimer ? C'est une belle idiome !

383voto

Willi aus Rohr Points 1744

Si vous êtes prêt à utiliser une bibliothèque externe, consultez le site suivant Apache Commons IO (JAR 200KB). Il contient un org.apache.commons.io.FileUtils.readFileToString() qui vous permet de lire l'intégralité d'un File en un String avec une seule ligne de code.

Exemple :

import java.io.*;
import java.nio.charset.*;
import org.apache.commons.io.*;

public String readFile() throws IOException {
    File file = new File("data.txt");
    return FileUtils.readFileToString(file, StandardCharsets.UTF_8);
}

187voto

Pablo Grisafi Points 2674

Une solution très légère basée sur Scanner :

Scanner scanner = new Scanner( new File("poem.txt") );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

Ou, si vous voulez définir le jeu de caractères :

Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" );
String text = scanner.useDelimiter("\\A").next();
scanner.close(); // Put this call in a finally block

Ou, avec un essayer avec les ressources qui appellera scanner.close() pour vous :

try (Scanner scanner = new Scanner( new File("poem.txt"), "UTF-8" )) {
    String text = scanner.useDelimiter("\\A").next();
}

N'oubliez pas que le Scanner peut lancer un IOException . Et n'oubliez pas d'importer java.io y java.util .

Fuente: Le blog de Pat Niemeyer

81voto

Dónal Points 61837

Si vous recherchez une alternative qui n'implique pas de bibliothèque tierce (par exemple, la bibliothèque de l'Office de l'aviation civile du Canada), vous pouvez utiliser la bibliothèque de l'Office de l'aviation civile du Canada. Commons I/O ), vous pouvez utiliser le Scanner classe :

private String readFile(String pathname) throws IOException {

    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int)file.length());        

    try (Scanner scanner = new Scanner(file)) {
        while(scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine() + System.lineSeparator());
        }
        return fileContents.toString();
    }
}

72voto

finnw Points 24592

Goyave possède une méthode similaire à celle de Commons IOUtils que Willi aus Rohr a mentionnée :

import com.google.common.base.Charsets;
import com.google.common.io.Files;

// ...

String text = Files.toString(new File(path), Charsets.UTF_8);

EDIT par PiggyPiglet
Files#toString est déprécié et doit être supprimé en octobre 2019. Utilisez à la place Files.asCharSource(new File(path), StandardCharsets.UTF_8).read();

EDIT par Oscar Reyes

Il s'agit du code sous-jacent (simplifié) de la bibliothèque citée :

InputStream in = new FileInputStream(file);
byte[] b  = new byte[file.length()];
int len = b.length;
int total = 0;

while (total < len) {
  int result = in.read(b, total, len - total);
  if (result == -1) {
    break;
  }
  total += result;
}

return new String( b , Charsets.UTF_8 );

Modifier (par Jonik) : Ce qui précède ne correspond pas au code source des versions récentes de Guava. Pour la source actuelle, voir les classes Fichiers , CharStreams , Source d'octets y CharSource sur com.google.common.io paquet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X