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


J'utilise l'idiome ci-dessous depuis quelque temps maintenant. Et il semble que ce soit le plus répandu, du moins sur les sites que j'ai visités.

Y at-il une meilleure / différente façon de lire un fichier dans une chaîne 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();
    }
}

1210
2017-11-28 18:32


origine


Réponses:


Lire tout le texte d'un fichier

Voici un idiome compact et robuste pour Java 7, enveloppé 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 des lignes de texte à partir d'un fichier

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

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

En Java 8, BufferedReader ajouté une nouvelle méthode, lines() pour produire un Stream<String>. Si un IOException est rencontré lors de la lecture du fichier, il est enveloppé dans un UncheckedIOException, depuis Stream n'accepte pas les lambdas qui lancent des exceptions vérifiées.

try (BufferedReader r = Files.newBufferedReader(path, encoding)) {
  r.lines().forEach(System.out::println);
}

Il y a aussi Files.lines() méthode qui fait quelque chose de très similaire, en retournant la Stream<String> directement. Mais je ne l'aime pas. le Stream a besoin d'un close() appel; ceci est mal documenté sur l'API, et je suspecte beaucoup de gens de ne même pas remarquer Stream a un close() méthode. Donc, votre code serait très similaire, comme ceci:

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

La différence est que vous avez un Stream assigné à une variable, et j'essaie d'éviter cela comme une pratique pour que je n'essaie pas accidentellement d'invoquer le flux deux fois.

Utilisation de la mémoire

La première méthode, qui préserve les sauts de ligne, peut temporairement nécessiter plusieurs fois la taille du fichier, car le contenu du fichier brut (un tableau d'octets) et les caractères décodés (dont chacun est de 16 bits même s'ils sont codés) comme 8 bits dans le fichier) résident dans la mémoire à la fois. Il est plus sûr d'appliquer aux fichiers que vous savez être petit par rapport à la mémoire disponible.

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

Pour lire des fichiers volumineux, vous avez besoin d'une conception différente pour votre programme, qui lit un morceau de texte à partir d'un flux, le traite, puis passe à la suivante, en réutilisant le même bloc de mémoire de taille fixe. Ici, "grand" dépend des spécifications de l'ordinateur. De nos jours, ce seuil peut être plusieurs gigaoctets de RAM. La troisième méthode, utilisant un Stream<String> est une façon de le faire, si vos entrées "enregistrements" se trouvent être des lignes individuelles. (En utilisant le readLine() méthode de BufferedReader est l'équivalent procédural de cette approche.)

Encodage de caractère

Une chose qui manque à l'échantillon dans le message original est le codage de caractères. Il y a des cas spéciaux où la plate-forme par défaut est ce que vous voulez, mais ils sont rares, et vous devriez pouvoir justifier votre choix.

le StandardCharsets class définit des constantes pour les encodages requis de tous les runtimes Java:

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

La plate-forme par défaut est disponible à partir de la Charsetclasse lui-même:

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

Note: Cette réponse remplace en grande partie ma version Java 6. L'utilitaire de Java 7 simplifie le code en toute sécurité, 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 récupéré. Vous pouvez voir l'ancienne version via le lien "édité" sur cette réponse.


1251
2017-11-28 18:56



Chambre des communes FileUtils.readFileToString:

public static String readFileToString(File file)
                       throws IOException

Lit le contenu d'un fichier dans une chaîne en utilisant l'encodage par défaut   pour la VM. Le fichier est toujours fermé.

Paramètres:

  • file - le fichier à lire ne doit pas être nul

Résultats:   le contenu du fichier, jamais null

Jette:    - IOException - en cas d'erreur E / S

Depuis:   Commons IO 1.3.1

Le code utilisé (indirectement) par cette classe est:

IOUtils.java en dessous de Apache License 2.0.

public static long copyLarge(InputStream input, OutputStream output)
       throws IOException {
   byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
   long count = 0;
   int n = 0;
   while (-1 != (n = input.read(buffer))) {
       output.write(buffer, 0, n);
       count += n;
   }
   return count;
}

Il est très similaire à celui utilisé par Ritche_W.


296
2017-11-28 18:44



De cette page une solution très maigre:

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

ou

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

Si vous souhaitez définir le jeu de caractères


159
2017-09-16 20:02



Si vous recherchez une alternative qui n'implique pas de bibliothèque tierce (par ex. 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());
    Scanner scanner = new Scanner(file);
    String lineSeparator = System.getProperty("line.separator");

    try {
        while(scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine() + lineSeparator);
        }
        return fileContents.toString();
    } finally {
        scanner.close();
    }
}

68
2017-11-28 19:00



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

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

// ...

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

EDIT par Oscar Reyes

C'est le code sous-jacent (simplifié) sur 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 Des dossiers, CharStreams, ByteSource et CharSource dans com.google.common.io paquet.


63
2018-04-16 14:33



import java.nio.file.Files;

.......

 String readFile(String filename) {
            File f = new File(filename);
            try {
                byte[] bytes = Files.readAllBytes(f.toPath());
                return new String(bytes,"UTF-8");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
    }

50
2017-10-29 08:51



Si vous avez besoin d'un traitement de chaîne (traitement parallèle) Java 8 a la grande API Stream.

String result = Files.lines(Paths.get("file.txt"))
                    .parallel() // for parallel processing 
                    .map(String::trim) // to change line   
                    .filter(line -> line.length() > 2) // to filter some lines by a predicate                        
                    .collect(Collectors.joining()); // to join lines

D'autres exemples sont disponibles dans les exemples JDK sample/lambda/BulkDataOperations qui peut être téléchargé à partir de Oracle Java SE 8 page de téléchargement 

Un autre exemple de doublure

String out = String.join("\n", Files.readAllLines(Paths.get("file.txt")));

44
2017-11-28 19:56



Ce code va normaliser les sauts de ligne, ce qui peut être ou ne pas être ce que vous voulez vraiment faire.

Voici une alternative qui ne fait pas cela, et qui est (IMO) plus simple à comprendre que le code NIO (bien qu'il utilise encore java.nio.charset.Charset):

public static String readFile(String file, String csName)
            throws IOException {
    Charset cs = Charset.forName(csName);
    return readFile(file, cs);
}

public static String readFile(String file, Charset cs)
            throws IOException {
    // No real need to close the BufferedReader/InputStreamReader
    // as they're only wrapping the stream
    FileInputStream stream = new FileInputStream(file);
    try {
        Reader reader = new BufferedReader(new InputStreamReader(stream, cs));
        StringBuilder builder = new StringBuilder();
        char[] buffer = new char[8192];
        int read;
        while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
            builder.append(buffer, 0, read);
        }
        return builder.toString();
    } finally {
        // Potential issue here: if this throws an IOException,
        // it will mask any others. Normally I'd use a utility
        // method which would log exceptions and swallow them
        stream.close();
    }        
}

44
2017-10-28 07:04