Come posso leggere un grande file di testo riga per riga usando Java?


848

Ho bisogno di leggere un grande file di testo di circa 5-6 GB riga per riga usando Java.

Come posso farlo rapidamente?


69
@kamaci et. al. Questa domanda non deve essere contrassegnata come duplicata. "Leggi rapidamente l'ultima riga" non è un'alternativa ed è discutibile se sia "Il modo più veloce per leggere file di testo riga per riga". Il modo più rapido di fare qualcosa non è necessariamente il modo comune. Inoltre, le risposte che seguono includono il codice, l'alternativa più pertinente che elenchi no. Questa domanda è utile Attualmente è il principale risultato di ricerca di Google per "Java leggere il file riga per riga". Infine, è scoraggiante arrivare all'overflow dello stack e scoprire che 1 su ogni 2 domande è contrassegnato per lo smaltimento.
Patrick Cullen,

5
Ecco un confronto della velocità per sei possibili implementazioni.
Serg M Ten,

4
Evento, anche se ho letto commenti sostenendo che la politica stretta di SO fa schifo, SO persiste. È una prospettiva di sviluppo così ristretta da voler evitare la ridondanza a tutti i costi! Lascia correre! La crema salirà verso l'alto e lo sh * t affonderà sul fondo bene da solo. Anche se una domanda potrebbe essere stata posta prima (quale domanda non lo è ??), ciò non significa che una nuova domanda potrebbe non essere in grado di formularla meglio, ottenere risposte migliori, posizionarsi più in alto nei motori di ricerca ecc. È interessante notare che questo la domanda ora è "protetta" ....
Stijn de Witt,

3
È incredibile come le domande vengano contrassegnate come duplicate semplicemente leggendo il titolo.
Luca,

Risposte:


1064

Un modello comune è quello di utilizzare

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

Puoi leggere i dati più velocemente se pensi che non ci sia la codifica dei caratteri. ad esempio ASCII-7 ma non farà molta differenza. È molto probabile che ciò che fai con i dati richiederà molto più tempo.

EDIT: un modello meno comune da utilizzare che evita la portata delle lineperdite.

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

AGGIORNAMENTO: In Java 8 puoi farlo

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

NOTA: è necessario posizionare lo Stream in un blocco try-with-resource per assicurarsi che venga richiamato il metodo #close, altrimenti l'handle del file sottostante non verrà mai chiuso fino a quando GC non lo farà molto più tardi.


6
Che aspetto ha questo modello con una corretta gestione delle eccezioni? Noto che br.close () genera IOException, il che sembra sorprendente - cosa potrebbe accadere quando si chiude un file aperto per la lettura, comunque? Il costruttore di FileReader potrebbe generare un'eccezione FileNotFound.
MikeB,

3
Se ho un file da 200 MB e può leggere a 90 MB / s, mi aspetto che impieghi ~ 3 secondi? Il mio sembra richiedere alcuni minuti, con questo modo "lento" di leggere. Sono su un SSD, quindi la velocità di lettura non dovrebbe essere un problema?
Jiew Meng

4
@JiewMeng Quindi sospetto che qualcos'altro che stai facendo sia prendere tempo. Puoi provare a leggere solo le righe del file e nient'altro .
Peter Lawrey,

44
Perché non for(String line = br.readLine(); line != null; line = br.readLine())Btw, in Java 8 puoi fare try( Stream<String> lines = Files.lines(...) ){ for( String line : (Iterable<String>) lines::iterator ) { ... } }che è difficile non odiare.
Aleksandr Dubinsky,

26
@AleksandrDubinsky Il problema che ho con le chiusure in Java 8 è che rende molto più semplice la lettura del codice (oltre ad essere più lento). Posso vedere molti sviluppatori che lo usano troppo perché è "bello".
Peter Lawrey,

155

Guarda questo blog:

È possibile specificare la dimensione del buffer o utilizzare la dimensione predefinita. L'impostazione predefinita è abbastanza grande per la maggior parte degli scopi.

// 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
Il mio file è 1,5 Gig e non è possibile leggere il file usando la tua risposta!
Aboozar Rajabi,

3
@AboozarRajabi Certo che è possibile. Questo codice può leggere qualsiasi file di testo.
Marchese di Lorne,

10
Sottovalutato per link di scarsa qualità. C'è un completamente inutile DataInputStreame lo stream sbagliato è chiuso. Nulla di sbagliato con il tutorial Java, e non c'è bisogno di citare rifiuti Internet arbitrari di terze parti come questo.
Marchese di Lorne,

1
Eliminerei i commenti, hai 4 righe di commenti ridondanti al 100% per 6 righe di codice.
Buffalo,

98

Una volta uscito Java 8 (marzo 2014) sarai in grado di usare i flussi:

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

Stampa di tutte le righe nel file:

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

1
Usa StandardCharsets.UTF_8, usa Stream<String>per concisione ed evita di usare forEach()e soprattutto a forEachOrdered()meno che non ci sia una ragione.
Aleksandr Dubinsky,

2
Perché evitare forEach ()? È male?
steventrouble

Se io per noi invece che per ogni ordine, le righe potrebbero essere stampate in modo anomalo, no?
msayag

2
@steventrouble Dai un'occhiata a: stackoverflow.com/questions/16635398/… Non è male se passi un breve riferimento di funzione come forEach(this::process), ma diventa brutto se scrivi blocchi di codice come lambdas all'interno forEach().
Aleksandr Dubinsky

2
@msayag, hai ragione, è necessario forEachOrderedper eseguire in ordine. Tieni presente che in quel caso non sarai in grado di parallelizzare lo stream, anche se ho scoperto che la parallelizzazione non si attiva a meno che il file non contenga migliaia di righe.
Aleksandr Dubinsky

38

Ecco un esempio con la gestione completa degli errori e il supporto delle specifiche del set di caratteri per pre-Java 7. Con Java 7 è possibile utilizzare la sintassi di prova con risorse, che rende il codice più pulito.

Se si desidera solo il set di caratteri predefinito, è possibile saltare InputStream e utilizzare 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 */ } }
}

Ecco la versione Groovy, con gestione completa degli errori:

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

1
Cosa ha a che fare un ByteArrayInputStreamfeed con una stringa letterale con la lettura di un file di testo di grandi dimensioni?
Marchese di Lorne,

chiude assolutamente inutile. Non vi è alcun motivo per chiudere ogni flusso. Se chiudi uno di questi flussi, chiudi automaticamente tutti gli altri flussi ...
Enerccio,

21

In Java 8, puoi fare:

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

Alcune note: lo stream restituito da Files.lines(diversamente dalla maggior parte degli stream) deve essere chiuso. Per i motivi menzionati qui evito di usare forEach(). Lo strano codice (Iterable<String>) lines::iteratorlancia uno Stream su un Iterable.


Non implementando Iterablequesto codice è definitivamente brutto sebbene utile. Ha bisogno di un cast (cioè (Iterable<String>)) per funzionare.
Stephan,

Come posso saltare la prima riga con questo metodo?
qed

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

1
Se non hai intenzione di utilizzare effettivamente le Streamfunzionalità, usare Files.newBufferedReaderinvece di Files.linese chiamare ripetutamente readLine()fino a quando nullinvece di usare costrutti come (Iterable<String>) lines::iteratorsembra essere molto più semplice ...
Holger,

Perché usi :: in lines :: iterator? L'unico uso che conosco per :: è di impacchettare il nome del metodo nella funzione lambda. In per il parametro loop dopo: dovrebbe essere variabile mentre si ottiene un metodo lambda utilizzando ::
Trismegistos

19

Quello che puoi fare è scansionare l'intero testo usando Scanner e scorrere il testo riga per riga. Ovviamente dovresti importare quanto segue:

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
    }
}

Lo scanner fondamentalmente scansiona tutto il testo. Il ciclo while viene utilizzato per attraversare l'intero testo.

La .hasNextLine()funzione è un valore booleano che restituisce true se ci sono ancora più righe nel testo. La .nextLine()funzione ti fornisce un'intera riga come stringa che puoi quindi usare nel modo desiderato. Prova System.out.println(line)a stampare il testo.

Nota a margine: .txt è il testo del tipo di file.


La dichiarazione del metodo non dovrebbe apparire al posto di questa: ´pubblicato statico vuoto readText genera FileNotFoundException () {´ Come: ´pubblicito statico vuoto readText () genera FileNotFoundException {´
Ketcomp

Questo è notevolmente più lento di BufferedReader.readLine(), e ha chiesto il metodo più performante.
Marchese di Lorne,

18

FileReader non ti consente di specificare la codifica, utilizzare InputStreamReaderinvece se è necessario specificarlo:

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();
}

Se hai importato questo file da Windows, potrebbe avere la codifica ANSI (Cp1252), quindi devi specificare la codifica.


17

Ho documentato e testato 10 modi diversi per leggere un file in Java e poi li ho eseguiti l'uno contro l'altro facendoli leggere in file di test da 1KB a 1GB. Ecco i 3 metodi di lettura dei file più veloci per la lettura di un file di test da 1 GB.

Si noti che durante l'esecuzione dei test delle prestazioni non ho prodotto nulla sulla console poiché ciò rallenterebbe davvero il test. Volevo solo testare la velocità di lettura pura.

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

Testato in Java 7, 8, 9. Questo è stato nel complesso il metodo più veloce. La lettura di un file da 1 GB era costantemente poco meno di 1 secondo.

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 ()

Questo è stato testato con successo in Java 8 e 9 ma non funzionerà in Java 7 a causa della mancanza di supporto per le espressioni lambda. Ci sono voluti circa 3,5 secondi per leggere in un file da 1 GB che lo metteva al secondo posto per quanto riguarda la lettura di file più grandi.

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

Testato per funzionare in Java 7, 8, 9. Questa lettura ha richiesto circa 4,5 secondi in un file di test da 1 GB.

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);
      }
    }
  }

Puoi trovare le classifiche complete per tutti i 10 metodi di lettura dei file qui .


1
La tua guida è fantastica :)
Faisal Julaidan,

Per lo più stai programmando System.out.print/println()qui; stai anche assumendo che il file si adatti alla memoria nei tuoi primi due casi.
Marchese di Lorne,

Giusto. Forse avrei potuto rendere quelle ipotesi più esplicite nella mia risposta.
Gomisha,

16

In 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
attenzione! usando line.split in questo modo NON verrà analizzato correttamente se un campo contiene una virgola ed è racchiuso tra virgolette. Questa divisione lo ignorerà e separerà semplicemente il campo in blocchi usando la virgola interna. HTH, Marcelo.
Marcelo Finki,

CSV: file Valori separati da virgola, quindi non dovresti usare la virgola in un campo CSV, a meno che tu non intenda aggiungere un altro campo. Quindi, usa split per il token virgola in java quando l'analisi di un file CSV è perfettamente corretta e corretta
Diego Duarte,

7
Diego, questo non è corretto. L'unico standard CSV (RFC 4180) dice specificamente "I campi che contengono interruzioni di riga (CRLF), virgolette doppie e virgole devono essere racchiusi tra virgolette doppie".
serg.nechaev,

2
Utilizzare StandardCharsets.UTF_8per evitare l'eccezione verificata inCharset.forName("UTF-8")
Aleksandr Dubinsky

2
Grazie "Diego Duarte" per il tuo commento; devo dire che sono d'accordo con le risposte "serg.nechaev". Vedo le virgole incorporate nei file CSV 'tutto il tempo'. Le persone si aspettano che questo sarà accettato. con tutto il dovuto rispetto. anche un grande grazie a "serg.nechaev". IMHO hai ragione. Saluta tutti.
Marcelo Finki,

13

In Java 8, esiste anche un'alternativa all'utilizzo Files.lines(). Se la tua sorgente di input non è un file ma qualcosa di più astratto come un Readero un InputStream, puoi eseguire lo streaming delle linee tramite il metodo BufferedReaders lines().

Per esempio:

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

chiamerà processLine()per ogni linea di input letta da BufferedReader.


10

Per leggere un file con 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

È possibile utilizzare la classe Scanner

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

2
@ Tim 'Orribile bomba' non è un termine che riconosco in CS. Cosa intendi esattamente?
Marchese di Lorne,

Scava giù, esegui molto lentamente, molto probabilmente crash. Probabilmente dovrei evitare i modi di dire su questo sito;)
Tim

4
@Tim Perché dovrebbe farlo?
xehpuk,

2
L'uso Scannerva bene, ma questa risposta non include il codice completo per usarlo correttamente.
Aleksandr Dubinsky

5
@Tim Questo codice non farà né "bombardare orribilmente" né "impantanarsi" né "eseguire molto lentamente" né "molto probabilmente si schianterà". In effetti, come scritto, leggerà solo una riga, quasi istantaneamente. Puoi leggere i megabyte al secondo in questo modo, sebbene BufferedReader.readLine()sia certamente più volte più veloce. Se la pensi diversamente, specifica i motivi.
Marchese di Lorne,

7

Devi usare il readLine()metodo in class BufferedReader. Crea un nuovo oggetto da quella classe e utilizza questo metodo su di lui e salvalo in una stringa.

BufferReader Javadoc


Sembra che il collegamento a BufferReaderAPI sia interrotto
Sandeep,

6

Il modo chiaro per raggiungere questo obiettivo,

Per esempio:

Se hai dataFile.txtsulla tua directory corrente

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();
    }
}

L'output come di seguito, inserisci qui la descrizione dell'immagine


Perché è più chiaro? E non pubblicare qui immagini di testo. Pubblica il testo.
Marchese di Lorne,

Hai pubblicato una foto. È un'immagine del testo. Avresti potuto tagliare e incollare il testo direttamente in questa pagina. Nessuno ha detto nulla sulla pubblicazione di programmi. Pubblicare immagini di testo è una perdita del tuo tempo, di cui non mi interessa, e della tua larghezza di banda, cosa che faccio.
Marchese di Lorne,

6

Java 9:

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

2
Penso che deviSystem.getProperty("os.name").equals("Linux")
SpringLearner

5
Non confrontare le stringhe con ==!
JonasCz - Ripristina Monica il

6
Questo è l'esempio canonico di Java 8, come già pubblicato da altri. Perché affermi che questo è "Java-9"?
Holger,

I file di memoria @Holger mappati che ha dimenticato di menzionare potrebbero essere?
Eugene,

per elaborarlo riga per riga puoi provare (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");
}

Per me funziona. Spero che possa aiutarti anche tu.


3

Puoi usare gli stream per farlo in modo più preciso:

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

2
Sono d'accordo che in realtà va bene. Inoltre, alla gente non piace a causa della strana scelta di StringBuffer (StringBuilder è generalmente preferito, anche se potrebbe essere solo un brutto nome per variabile). Anche perché è già menzionato sopra.
Andrii Rubtsov,

2

Di solito faccio la routine di lettura semplice:

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

Puoi usare questo codice:

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();
        }

    }

}

Una spiegazione sarebbe in ordine.
Peter Mortensen,

0

Utilizzando il pacchetto org.apache.commons.io , ha fornito maggiori prestazioni, soprattutto nel codice legacy che utilizza Java 6 e versioni precedenti.

Java 7 ha un'API migliore con meno gestione delle eccezioni e metodi più utili:

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);
}

Esperto di

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

0

Puoi anche usare 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)è un metodo deprecato. Inoltre, viene invocato il metodo IOUtils.readLines, che utilizza BufferedReader e ArrayList. Questo non è un metodo riga per riga, e certamente non uno che sarebbe pratico per leggere diversi GB.
vallismortis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.