Comment puis-je lire un grand fichier texte ligne par ligne en utilisant Java?


848

J'ai besoin de lire un gros fichier texte d'environ 5-6 Go ligne par ligne en utilisant Java.

Comment puis-je le faire rapidement?


69
@kamaci et. Al. Cette question ne doit pas être marquée comme doublon. "Lire rapidement la dernière ligne" n'est pas une alternative, et son discutable si "la manière la plus rapide de lire le fichier texte ligne par ligne" l'est. La façon la plus rapide de faire quelque chose n'est pas nécessairement la voie commune. En outre, les réponses ci-dessous incluent du code, ce qui n'est pas le cas pour l'alternative la plus pertinente. Cette question est utile. C'est actuellement le meilleur résultat de recherche google pour "java read file line by line". Enfin, il est rebutant d'arriver au débordement de pile et de constater qu'une question sur 2 est signalée pour élimination.
Patrick Cullen

5
Voici une comparaison de la vitesse pour six implémentations possibles.
Serg M Ten

4
Bien que j'aie lu des commentaires faisant valoir que la politique étroite de SO est nulle, SO y persiste. C'est une perspective de développeur si bornée de vouloir éviter la redondance à tout prix! Juste le laisser être! La crème se lèvera vers le haut et le sh * t coulera au fond très bien tout seul. Même si une question a déjà été posée (laquelle ne l'est pas ??), cela ne signifie pas qu'une nouvelle question ne pourra pas mieux la formuler, obtenir de meilleures réponses, se classer plus haut dans les moteurs de recherche, etc. question est désormais «protégée» ....
Stijn de Witt

3
C'est incroyable de voir comment les questions sont marquées en double en lisant simplement le titre.
Luke

Réponses:


1064

Un modèle courant consiste à utiliser

try (BufferedReader br = new BufferedReader(new FileReader(file))) {
    String line;
    while ((line = br.readLine()) != null) {
       // process the line.
    }
}

Vous pouvez lire les données plus rapidement si vous supposez qu'il n'y a pas de codage de caractères. par exemple ASCII-7 mais cela ne fera pas beaucoup de différence. Il est fort probable que ce que vous faites avec les données prenne beaucoup plus de temps.

EDIT: un modèle moins courant à utiliser qui évite l'ampleur des linefuites.

try(BufferedReader br = new BufferedReader(new FileReader(file))) {
    for(String line; (line = br.readLine()) != null; ) {
        // process the line.
    }
    // line is not visible here.
}

MISE À JOUR: Dans Java 8, vous pouvez faire

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
        stream.forEach(System.out::println);
}

REMARQUE: Vous devez placer le Stream dans un bloc try-with-resource pour vous assurer que la méthode #close est appelée dessus, sinon le descripteur de fichier sous-jacent n'est jamais fermé jusqu'à ce que GC le fasse beaucoup plus tard.


6
À quoi ressemble ce modèle avec une gestion des exceptions appropriée? Je note que br.close () lève IOException, ce qui semble surprenant - que pourrait-il se passer lors de la fermeture d'un fichier ouvert en lecture, de toute façon? Le constructeur de FileReader peut lever une exception FileNotFound.
MikeB

3
Si j'ai un fichier de 200 Mo et qu'il peut lire à 90 Mo / s, alors je m'attends à ce qu'il prenne ~ 3 s? La mienne semble prendre quelques minutes, avec cette lecture "lente". Je suis sur un SSD donc les vitesses de lecture ne devraient pas être un problème?
Jiew Meng

4
@JiewMeng SO Je soupçonne que quelque chose d'autre que vous faites prend du temps. Pouvez-vous essayer de simplement lire les lignes du fichier et rien d' autre.
Peter Lawrey

44
Pourquoi pas for(String line = br.readLine(); line != null; line = br.readLine())Btw, en Java 8 vous pouvez faire try( Stream<String> lines = Files.lines(...) ){ for( String line : (Iterable<String>) lines::iterator ) { ... } }ce qui est difficile à ne pas haïr.
Aleksandr Dubinsky

26
@AleksandrDubinsky Le problème que j'ai avec les fermetures dans Java 8 est qu'il rend très facilement le code plus compliqué à lire (en plus d'être plus lent) Je peux voir beaucoup de développeurs en abuser parce qu'il est "cool".
Peter Lawrey

155

Regardez ce blog:

La taille du tampon peut être spécifiée ou la taille par défaut peut être utilisée. La valeur par défaut est suffisamment grande pour la plupart des applications.

// Open the file
FileInputStream fstream = new FileInputStream("textfile.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));

String strLine;

//Read File Line By Line
while ((strLine = br.readLine()) != null)   {
  // Print the content on the console
  System.out.println (strLine);
}

//Close the input stream
fstream.close();

6
Mon fichier est de 1,5 Go et il n'est pas possible de lire le fichier en utilisant votre réponse!
Aboozar Rajabi

3
@AboozarRajabi Bien sûr, c'est possible. Ce code peut lire n'importe quel fichier texte.
Marquis de Lorne

10
A voté pour le lien de mauvaise qualité. Il y a un complètement inutile DataInputStream, et le mauvais flux est fermé. Rien de mal avec le tutoriel Java, et pas besoin de citer des déchets Internet tiers arbitraires comme celui-ci.
Marquis de Lorne

1
Je laisserais tomber les commentaires, vous avez 4 lignes de commentaires 100% redondants pour 6 lignes de code.
Buffalo

98

Une fois Java 8 sorti (mars 2014), vous pourrez utiliser des flux:

try (Stream<String> lines = Files.lines(Paths.get(filename), Charset.defaultCharset())) {
  lines.forEachOrdered(line -> process(line));
}

Impression de toutes les lignes du fichier:

try (Stream<String> lines = Files.lines(file, Charset.defaultCharset())) {
  lines.forEachOrdered(System.out::println);
}

1
Utilisez StandardCharsets.UTF_8, utilisez Stream<String>pour la concision, et évitez d'utiliser forEach()et surtout à forEachOrdered()moins qu'il y ait une raison.
Aleksandr Dubinsky

2
Pourquoi éviter forEach ()? Est-il mauvais?
steventrouble

Si je nous forEach au lieu de forEachOrdered, les lignes peuvent être imprimées dans le désordre, n'est-ce pas?
msayag

2
@steventrouble Jetez un oeil à: stackoverflow.com/questions/16635398/… Ce n'est pas mal si vous passez une référence de fonction courte comme forEach(this::process), mais cela devient moche si vous écrivez des blocs de code en tant que lambdas à l'intérieur forEach().
Aleksandr Dubinsky

2
@msayag, vous avez raison, vous avez besoin forEachOrderedpour exécuter dans l'ordre. Sachez que vous ne pourrez pas paralléliser le flux dans ce cas, même si j'ai constaté que la parallélisation ne s'active que si le fichier contient des milliers de lignes.
Aleksandr Dubinsky

38

Voici un exemple avec gestion complète des erreurs et prise en charge de la spécification de jeu de caractères pour pré-Java 7. Avec Java 7, vous pouvez utiliser la syntaxe try-with-resources, qui rend le code plus propre.

Si vous voulez juste le jeu de caractères par défaut, vous pouvez ignorer InputStream et utiliser FileReader.

InputStream ins = null; // raw byte-stream
Reader r = null; // cooked reader
BufferedReader br = null; // buffered for readLine()
try {
    String s;
    ins = new FileInputStream("textfile.txt");
    r = new InputStreamReader(ins, "UTF-8"); // leave charset out for default
    br = new BufferedReader(r);
    while ((s = br.readLine()) != null) {
        System.out.println(s);
    }
}
catch (Exception e)
{
    System.err.println(e.getMessage()); // handle exception
}
finally {
    if (br != null) { try { br.close(); } catch(Throwable t) { /* ensure close happens */ } }
    if (r != null) { try { r.close(); } catch(Throwable t) { /* ensure close happens */ } }
    if (ins != null) { try { ins.close(); } catch(Throwable t) { /* ensure close happens */ } }
}

Voici la version Groovy, avec une gestion complète des erreurs:

File f = new File("textfile.txt");
f.withReader("UTF-8") { br ->
    br.eachLine { line ->
        println line;
    }
}

1
Qu'est-ce qu'un ByteArrayInputStreamlittéral alimenté par une chaîne a à voir avec la lecture d'un gros fichier texte?
Marquis de Lorne

ferme absolument inutile. Il n'y a aucune raison de fermer chaque flux. Si vous fermez l'un de ces flux, vous fermez automatiquement tous les autres flux ...
Enerccio

21

En Java 8, vous pourriez faire:

try (Stream<String> lines = Files.lines (file, StandardCharsets.UTF_8))
{
    for (String line : (Iterable<String>) lines::iterator)
    {
        ;
    }
}

Quelques notes: Le flux renvoyé par Files.lines(contrairement à la plupart des flux) doit être fermé. Pour les raisons mentionnées ici, j'évite d'utiliser forEach(). L'étrange code (Iterable<String>) lines::iteratortransforme un Stream en Iterable.


En ne mettant pas en œuvre Iterablece code est définitivement moche bien qu'utile. Il a besoin d'un casting (ie (Iterable<String>)) pour fonctionner.
Stephan

Comment puis-je sauter la première ligne avec cette méthode?
qed

2
@qedfor(String line : (Iterable<String>) lines.skip(1)::iterator)
Aleksandr Dubinsky

1
Si vous n'avez pas l'intention d'utiliser réellement des Streamfonctionnalités, utiliser Files.newBufferedReaderau lieu d' Files.linesappeler et répéter readLine()jusqu'à ce que, nullau lieu d'utiliser des constructions comme (Iterable<String>) lines::iteratorsemble être beaucoup plus simple…
Holger

Pourquoi utilisez-vous :: in lines :: iterator? La seule utilisation que je connaisse pour :: est de regrouper le nom de la méthode dans la fonction lambda. Dans le paramètre de boucle après: devrait être variable pendant que vous obtenez une méthode lambda en utilisant ::
Trismegistos

19

Ce que vous pouvez faire est de numériser le texte entier à l'aide du scanner et de parcourir le texte ligne par ligne. Bien sûr, vous devez importer les éléments suivants:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public static void readText throws FileNotFoundException {
    Scanner scan = new Scanner(new File("samplefilename.txt"));
    while(scan.hasNextLine()){
        String line = scan.nextLine();
        //Here you can manipulate the string the way you want
    }
}

Le scanner scanne essentiellement tout le texte. La boucle while est utilisée pour parcourir tout le texte.

La .hasNextLine()fonction est un booléen qui renvoie true s'il y a encore plus de lignes dans le texte. La .nextLine()fonction vous donne une ligne entière sous forme de chaîne que vous pouvez ensuite utiliser comme vous le souhaitez. Essayez System.out.println(line)d'imprimer le texte.

Note latérale: .txt est le texte du type de fichier.


La déclaration de méthode ne devrait pas ressembler à ceci: ´public static void readText throws FileNotFoundException () {´ Like: ´public static void readText () throws FileNotFoundException {´
Ketcomp

C'est beaucoup plus lent que cela BufferedReader.readLine(), et il a demandé la méthode la plus performante.
Marquis de Lorne

18

FileReader ne vous permet pas de spécifier l'encodage, utilisez-le à la InputStreamReaderplace si vous devez le spécifier:

try {
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "Cp1252"));         

    String line;
    while ((line = br.readLine()) != null) {
        // process the line.
    }
    br.close();

} catch (IOException e) {
    e.printStackTrace();
}

Si vous avez importé ce fichier à partir de Windows, il peut avoir un encodage ANSI (Cp1252), vous devez donc spécifier l'encodage.


17

J'ai documenté et testé 10 façons différentes de lire un fichier en Java , puis je les ai exécutées les unes contre les autres en les faisant lire des fichiers de test de 1 Ko à 1 Go. Voici les 3 méthodes de lecture de fichiers les plus rapides pour lire un fichier de test de 1 Go.

Notez que lors de l'exécution des tests de performances, je n'ai rien sorti sur la console car cela ralentirait vraiment le test. Je voulais juste tester la vitesse de lecture brute.

1) java.nio.file.Files.readAllBytes ()

Testé en Java 7, 8, 9. C'était globalement la méthode la plus rapide. La lecture d'un fichier de 1 Go était systématiquement un peu moins d'une seconde.

import java.io..File;
import java.io.IOException;
import java.nio.file.Files;

public class ReadFile_Files_ReadAllBytes {
  public static void main(String [] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-1GB.txt";
    File file = new File(fileName);

    byte [] fileBytes = Files.readAllBytes(file.toPath());
    char singleChar;
    for(byte b : fileBytes) {
      singleChar = (char) b;
      System.out.print(singleChar);
    }
  }
}

2) java.nio.file.Files.lines ()

Cela a été testé avec succès en Java 8 et 9, mais cela ne fonctionnera pas en Java 7 en raison du manque de prise en charge des expressions lambda. Il a fallu environ 3,5 secondes pour lire un fichier de 1 Go, ce qui l'a placé en deuxième position pour la lecture de fichiers plus volumineux.

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.stream.Stream;

public class ReadFile_Files_Lines {
  public static void main(String[] pArgs) throws IOException {
    String fileName = "c:\\temp\\sample-1GB.txt";
    File file = new File(fileName);

    try (Stream linesStream = Files.lines(file.toPath())) {
      linesStream.forEach(line -> {
        System.out.println(line);
      });
    }
  }
}

3) BufferedReader

Testé pour fonctionner en Java 7, 8, 9. Cela a pris environ 4,5 secondes pour lire dans un fichier de test de 1 Go.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFile_BufferedReader_ReadLine {
  public static void main(String [] args) throws IOException {
    String fileName = "c:\\temp\\sample-1GB.txt";
    FileReader fileReader = new FileReader(fileName);

    try (BufferedReader bufferedReader = new BufferedReader(fileReader)) {
      String line;
      while((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
      }
    }
  }

Vous pouvez trouver le classement complet des 10 méthodes de lecture de fichiers ici .


1
Votre guide est incroyable :)
Faisal Julaidan

Vous chronométrez principalement System.out.print/println()ici; vous supposez également que le fichier tiendra dans la mémoire dans vos deux premiers cas.
Marquis de Lorne

C'est suffisant. J'aurais peut-être pu rendre ces hypothèses plus explicites dans ma réponse.
gomisha

16

En Java 7:

String folderPath = "C:/folderOfMyFile";
Path path = Paths.get(folderPath, "myFileName.csv"); //or any text file eg.: txt, bat, etc
Charset charset = Charset.forName("UTF-8");

try (BufferedReader reader = Files.newBufferedReader(path , charset)) {
  while ((line = reader.readLine()) != null ) {
    //separate all csv fields into string array
    String[] lineVariables = line.split(","); 
  }
} catch (IOException e) {
    System.err.println(e);
}

9
être conscient! utiliser line.split de cette façon ne sera PAS analysé correctement si un champ contient une virgule et qu'il est entouré de guillemets. Cette division l'ignorera et séparera simplement le champ en morceaux à l'aide de la virgule interne. HTH, Marcelo.
Marcelo Finki

CSV: fichier de valeurs séparées par des virgules, vous ne devez donc pas utiliser de virgule dans un champ csv, sauf si vous souhaitez ajouter un autre champ. Donc, utilisez le fractionnement pour le jeton virgule en java lorsque l'analyse d'un fichier CSV est parfaitement correcte
Diego Duarte

7
Diego, ce n'est pas correct. La seule norme CSV (RFC 4180) indique spécifiquement que "les champs contenant des sauts de ligne (CRLF), des guillemets doubles et des virgules doivent être placés entre guillemets doubles."
serg.nechaev

2
Utilisez StandardCharsets.UTF_8pour éviter l'exception cochée dansCharset.forName("UTF-8")
Aleksandr Dubinsky

2
Merci "Diego Duarte" pour ton commentaire; je dois dire que je suis d'accord avec ce que "serg.nechaev" répond. Je vois des virgules intégrées dans les fichiers csv «tout le temps». Les gens s'attendent à ce que cela soit accepté. avec tout mon respect. aussi un grand merci à "serg.nechaev". À mon humble avis, vous avez raison. Cheerse tout le monde.
Marcelo Finki

13

Dans Java 8, il existe également une alternative à l'utilisation Files.lines(). Si votre source d'entrée n'est pas un fichier mais quelque chose de plus abstrait comme un Readerou un InputStream, vous pouvez diffuser les lignes via la méthode BufferedReaders lines().

Par exemple:

try (BufferedReader reader = new BufferedReader(...)) {
  reader.lines().forEach(line -> processLine(line));
}

appellera processLine()pour chaque ligne d'entrée lue par le BufferedReader.


10

Pour lire un fichier avec Java 8

package com.java.java8;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

/**
 * The Class ReadLargeFile.
 *
 * @author Ankit Sood Apr 20, 2017
 */
public class ReadLargeFile {

    /**
     * The main method.
     *
     * @param args
     *            the arguments
     */
    public static void main(String[] args) {
        try {
            Stream<String> stream = Files.lines(Paths.get("C:\\Users\\System\\Desktop\\demoData.txt"));
            stream.forEach(System.out::println);
        }
        catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

9

Vous pouvez utiliser la classe Scanner

Scanner sc=new Scanner(file);
sc.nextLine();

2
@Tim 'Bomb horribly' n'est pas un terme que je reconnais dans CS. Que veux-tu dire exactement?
Marquis de Lorne

Tournez-vous vers le bas, exécutez très lentement, le crash le plus probable. Je devrais probablement éviter les idiomes sur ce site;)
Tim

4
@Tim Pourquoi le ferait-il?
xehpuk

2
L'utilisation Scannerest très bien, mais cette réponse n'inclut pas le code complet pour l'utiliser correctement.
Aleksandr Dubinsky

5
@Tim Ce code ne "bombardera pas horriblement" ni "ne s'embourbera" ni "ne s'exécutera très lentement" ni "ne se plantera plus". En fait, tel qu'il est écrit, il ne lira qu'une seule ligne, presque instantanément. Vous pouvez lire des mégaoctets par seconde de cette façon, bien que ce BufferedReader.readLine()soit certainement plusieurs fois plus rapide. Si vous pensez le contraire, veuillez fournir vos raisons.
Marquis de Lorne

7

Vous devez utiliser la readLine()méthode dans class BufferedReader. Créez un nouvel objet à partir de cette classe et opérez cette méthode sur lui et enregistrez-le dans une chaîne.

BufferReader Javadoc


Il semble que le lien vers BufferReaderAPI soit rompu
Sandeep

6

La manière claire d'y parvenir,

Par exemple:

Si vous avez dataFile.txtsur votre répertoire actuel

import java.io.*;
import java.util.Scanner;
import java.io.FileNotFoundException;

public class readByLine
{
    public readByLine() throws FileNotFoundException
    {
        Scanner linReader = new Scanner(new File("dataFile.txt"));

        while (linReader.hasNext())
        {
            String line = linReader.nextLine();
            System.out.println(line);
        }
        linReader.close();

    }

    public static void main(String args[])  throws FileNotFoundException
    {
        new readByLine();
    }
}

La sortie comme ci-dessous, entrez la description de l'image ici


Pourquoi est-ce plus clair? Et ne postez pas de photos de texte ici. Postez le texte.
Marquis de Lorne

Vous avez posté une photo. C'est une image de texte. Vous auriez pu couper et coller le texte directement dans cette page. Personne n'a dit quoi que ce soit sur l'affichage des programmes. La publication de photos de texte est une perte de temps, ce qui m'est égal, et de votre bande passante, ce que je fais.
Marquis de Lorne

6

Java 9:

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
    stream.forEach(System.out::println);
}

2
Je pense que vous devezSystem.getProperty("os.name").equals("Linux")
SpringLearner

5
Ne comparez pas les chaînes avec ==!
JonasCz

6
Ceci est l'exemple canonique de Java 8, comme déjà posté par d'autres. Pourquoi prétendez-vous qu'il s'agit de «Java-9»?
Holger

Les fichiers mappés en mémoire @Holger qu'il a oublié de mentionner sont peut-être?
Eugene

pour le traiter ligne par ligne, vous pouvez essayer (Stream <String> stream = Files.lines (Paths.get (inputFile))) {stream.forEach ((line) -> {System.out.println (line);} ); }
thanos.a

3
BufferedReader br;
FileInputStream fin;
try {
    fin = new FileInputStream(fileName);
    br = new BufferedReader(new InputStreamReader(fin));

    /*Path pathToFile = Paths.get(fileName);
    br = Files.newBufferedReader(pathToFile,StandardCharsets.US_ASCII);*/

    String line = br.readLine();
    while (line != null) {
        String[] attributes = line.split(",");
        Movie movie = createMovie(attributes);
        movies.add(movie);
        line = br.readLine();
    }
    fin.close();
    br.close();
} catch (FileNotFoundException e) {
    System.out.println("Your Message");
} catch (IOException e) {
    System.out.println("Your Message");
}

Ça marche pour moi. J'espère que cela vous aidera aussi.


3

Vous pouvez utiliser des flux pour le faire plus précisément:

Files.lines(Paths.get("input.txt")).forEach(s -> stringBuffer.append(s);

2
Je suis d'accord que c'est très bien. Encore une fois, les gens ne l'aiment pas en raison du choix étrange de StringBuffer (StringBuilder est généralement préféré, même si ce n'est peut-être qu'un mauvais nom pour une variable). Aussi parce que c'est déjà mentionné ci-dessus.
Andrii Rubtsov

2

Je fais habituellement la routine de lecture simple:

void readResource(InputStream source) throws IOException {
    BufferedReader stream = null;
    try {
        stream = new BufferedReader(new InputStreamReader(source));
        while (true) {
            String line = stream.readLine();
            if(line == null) {
                break;
            }
            //process line
            System.out.println(line)
        }
    } finally {
        closeQuiet(stream);
    }
}

static void closeQuiet(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ignore) {
        }
    }
}

0

Vous pouvez utiliser ce code:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadTextFile {

    public static void main(String[] args) throws IOException {

        try {

            File f = new File("src/com/data.txt");

            BufferedReader b = new BufferedReader(new FileReader(f));

            String readLine = "";

            System.out.println("Reading file using Buffered Reader");

            while ((readLine = b.readLine()) != null) {
                System.out.println(readLine);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

Une explication serait de mise.
Peter Mortensen

0

En utilisant le package org.apache.commons.io , il a donné plus de performances, en particulier dans le code hérité qui utilise Java 6 et inférieur.

Java 7 a une meilleure API avec moins de gestion des exceptions et des méthodes plus utiles:

LineIterator lineIterator = null;
try {
    lineIterator = FileUtils.lineIterator(new File("/home/username/m.log"), "windows-1256"); // The second parameter is optionnal
    while (lineIterator.hasNext()) {
        String currentLine = lineIterator.next();
        // Some operation
    }
}
finally {
    LineIterator.closeQuietly(lineIterator);
}

Maven

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

0

Vous pouvez également utiliser Apache Commons IO :

File file = new File("/home/user/file.txt");
try {
    List<String> lines = FileUtils.readLines(file);
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

3
FileUtils.readLines(file)est une méthode obsolète. En outre, la méthode appelle IOUtils.readLines, qui utilise un BufferedReader et ArrayList. Ce n'est pas une méthode ligne par ligne, et certainement pas une méthode qui serait pratique pour lire plusieurs Go.
vallismortis
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.