¿Cómo puedo leer un archivo de texto grande línea por línea usando Java?

848

Necesito leer un archivo de texto grande de alrededor de 5-6 GB línea por línea usando Java.

¿Cómo puedo hacer esto rápidamente?

manoj singh
fuente
69
@kamaci et. Alabama. Esta pregunta no debe marcarse como un duplicado. "Leer rápidamente la última línea" no es una alternativa, y es discutible si "La forma más rápida de leer el archivo de texto línea por línea" sí lo es. La forma más rápida de hacer algo no es necesariamente la forma común. Además, las respuestas a continuación incluyen código, la alternativa más relevante que usted enumera no. Esta pregunta es útil. Actualmente es el principal resultado de búsqueda de Google para "Java Read File línea por línea". Finalmente, es desagradable llegar al desbordamiento de la pila y encontrar que 1 de cada 2 preguntas está marcada para su eliminación.
Patrick Cullen
55
Aquí hay una comparación de velocidad para seis implementaciones posibles.
Serg M Ten
44
Aunque he estado leyendo comentarios argumentando que la política cercana de SO apesta, SO persiste en ello. ¡Es una perspectiva de desarrollador de mente tan estrecha querer evitar la redundancia a toda costa! ¡Sólo deja que sea! La crema se elevará hasta la parte superior y la mierda * se hundirá hasta el fondo por sí sola. Si bien es posible que se haya formulado una pregunta antes (¿cuál no es la pregunta?), Eso no significa que una nueva pregunta no pueda formularla mejor, obtener mejores respuestas, obtener una clasificación más alta en los motores de búsqueda, etc. Curiosamente, esto la pregunta ahora está 'protegida' ...
Stijn de Witt
3
Es increíble cómo las preguntas se marcan como duplicadas con solo leer el título.
Lucas

Respuestas:

1064

Un patrón común es usar

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

Puede leer los datos más rápido si supone que no hay codificación de caracteres. ej. ASCII-7 pero no hará mucha diferencia. Es muy probable que lo que haga con los datos tarde mucho más.

EDITAR: un patrón menos común para usar que evita el alcance de las linefugas.

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

ACTUALIZACIÓN: En Java 8 puedes hacer

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

NOTA: Debe colocar la secuencia en un bloque de prueba con recursos para asegurarse de que se invoque el método #close, de lo contrario, el identificador de archivo subyacente nunca se cerrará hasta que GC lo haga mucho más tarde.

Peter Lawrey
fuente
66
¿Cómo se ve este patrón con el manejo adecuado de excepciones? Observo que br.close () arroja IOException, lo que parece sorprendente: ¿qué podría suceder al cerrar un archivo que se abre para leer, de todos modos? El constructor de FileReader podría generar una excepción FileNotFound.
MikeB
3
Si tengo un archivo de 200 MB y puede leer a 90 MB / s, ¿espero que tarde unos 3 segundos? Los míos parecen tomar minutos, con esta forma de lectura "lenta". Estoy en un SSD, ¿entonces las velocidades de lectura no deberían ser un problema?
Jiew Meng
44
@JiewMeng SO. Sospecho que algo más que estás haciendo es tomar tiempo. ¿Puedes intentar simplemente leer las líneas del archivo y nada más?
Peter Lawrey
44
Por qué no for(String line = br.readLine(); line != null; line = br.readLine()), por cierto, en Java 8 puedes hacer lo try( Stream<String> lines = Files.lines(...) ){ for( String line : (Iterable<String>) lines::iterator ) { ... } }que es difícil de no odiar.
Aleksandr Dubinsky
26
@AleksandrDubinsky El problema que tengo con los cierres en Java 8 es que hace que el código sea más complicado de leer (además de ser más lento), puedo ver que muchos desarrolladores lo usan en exceso porque es "genial".
Peter Lawrey
155

Mira este blog:

Se puede especificar el tamaño del búfer o se puede usar el tamaño predeterminado. El valor predeterminado es lo suficientemente grande para la mayoría de los propósitos.

// 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();
Naveed
fuente
66
¡Mi archivo es de 1.5 Gig y no es posible leer el archivo usando su respuesta!
Aboozar Rajabi
3
@AboozarRajabi Por supuesto que es posible. Este código puede leer cualquier archivo de texto.
Marqués de Lorne
10
Votado negativamente por un enlace de baja calidad. Hay una completamente inútil DataInputStream, y se cierra la secuencia incorrecta. No hay nada malo con el Tutorial de Java, y no es necesario citar basura arbitraria de Internet de terceros como esta.
Marqués de Lorne
1
Olvidaría los comentarios, tiene 4 líneas de comentarios 100% redundantes para 6 líneas de código.
Buffalo
98

Una vez que Java 8 esté fuera (marzo de 2014), podrá usar transmisiones:

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

Imprimir todas las líneas en el archivo:

try (Stream<String> lines = Files.lines(file, Charset.defaultCharset())) {
  lines.forEachOrdered(System.out::println);
}
msayag
fuente
1
Use StandardCharsets.UTF_8, use Stream<String>por concisión, y evite usar forEach()y especialmente a forEachOrdered()menos que haya una razón.
Aleksandr Dubinsky
2
¿Por qué evitar forEach ()? ¿Es malo?
steventrouble
Si utilizo forEach en lugar de forEachOrdered, las líneas podrían imprimirse fuera de servicio, ¿no?
msayag
2
@steventrouble Eche un vistazo a: stackoverflow.com/questions/16635398/… No está mal si pasa una referencia breve de función como forEach(this::process), pero se pone feo si escribe bloques de código como lambdas en su interior forEach().
Aleksandr Dubinsky
2
@msayag, Tienes razón, lo necesitas forEachOrderedpara ejecutar en orden. Tenga en cuenta que no podrá paralelizar la transmisión en ese caso, aunque descubrí que la paralelización no se activa a menos que el archivo tenga miles de líneas.
Aleksandr Dubinsky
38

Aquí hay una muestra con manejo completo de errores y soporte de especificación de juego de caracteres para pre-Java 7. Con Java 7 puede usar la sintaxis de prueba con recursos, lo que hace que el código sea más limpio.

Si solo desea el conjunto de caracteres predeterminado, puede omitir InputStream y usar 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 */ } }
}

Aquí está la versión Groovy, con manejo completo de errores:

File f = new File("textfile.txt");
f.withReader("UTF-8") { br ->
    br.eachLine { line ->
        println line;
    }
}
Estrella negra
fuente
1
¿Qué tiene que ver un ByteArrayInputStreamliteral alimentado por una cadena con la lectura de un archivo de texto grande?
Marqués de Lorne
Cierres absolutamente inútiles. No hay ninguna razón para cerrar cada transmisión. Si cierra cualquiera de esas transmisiones, automáticamente cierra todas las demás transmisiones ...
Enerccio
21

En Java 8, podrías hacer:

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

Algunas notas: La secuencia devuelta por Files.lines(a diferencia de la mayoría de las secuencias) debe cerrarse. Por las razones mencionadas aquí , evito usar forEach(). El extraño código (Iterable<String>) lines::iteratorarroja un Stream a un Iterable.

Aleksandr Dubinsky
fuente
Al no implementar Iterableeste código es definitivamente feo aunque útil. Necesita un yeso (es decir (Iterable<String>)) para funcionar.
Stephan
¿Cómo puedo omitir la primera línea con este método?
qed
2
@qedfor(String line : (Iterable<String>) lines.skip(1)::iterator)
Aleksandr Dubinsky
1
Si no tiene la intención de usar Streamcaracterísticas, usar en Files.newBufferedReaderlugar de Files.linesy llamar repetidamente readLine()hasta que en nulllugar de usar construcciones como (Iterable<String>) lines::iteratorparece ser mucho más simple ...
Holger
¿Por qué usas :: en líneas :: iterador? El único uso que conozco para :: es empaquetar el nombre del método en la función lambda. En el parámetro for loop after: debe ser variable mientras obtienes algún método lambda usando ::
Trismegistos
19

Lo que puede hacer es escanear todo el texto con Scanner y recorrer el texto línea por línea. Por supuesto, debe importar lo siguiente:

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

El escáner básicamente escanea todo el texto. El bucle while se usa para recorrer todo el texto.

La .hasNextLine()función es un valor booleano que devuelve verdadero si aún hay más líneas en el texto. La .nextLine()función le proporciona una línea completa como una Cadena que luego puede usar de la manera que desee. Intenta System.out.println(line)imprimir el texto.

Nota al margen: .txt es el texto del tipo de archivo.

iskandarchacra
fuente
¿No debería verse la declaración del método en lugar de esto: ´public static void readText arroja FileNotFoundException () {´ Like: ´public static void readText () arroja FileNotFoundException {´
Ketcomp
Esto es considerablemente más lento que BufferedReader.readLine(), y pidió el método con mejor rendimiento.
Marqués de Lorne
18

FileReader no le permitirá especificar la codificación, use InputStreamReaderen su lugar si necesita especificarla:

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 importó este archivo desde Windows, podría tener codificación ANSI (Cp1252), por lo que debe especificar la codificación.

vive el amor
fuente
17

Documenté y probé 10 formas diferentes de leer un archivo en Java y luego las comparé entre sí al hacer que leyeran en archivos de prueba de 1 KB a 1 GB. Estos son los métodos de lectura de 3 archivos más rápidos para leer un archivo de prueba de 1 GB.

Tenga en cuenta que cuando ejecuté las pruebas de rendimiento, no envié nada a la consola, ya que eso realmente ralentizaría la prueba. Solo quería probar la velocidad de lectura en bruto.

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

Probado en Java 7, 8, 9. Este fue en general el método más rápido. Leer un archivo de 1GB fue consistentemente un poco menos de 1 segundo.

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

Esto se probó con éxito en Java 8 y 9, pero no funcionará en Java 7 debido a la falta de soporte para expresiones lambda. Le tomó alrededor de 3.5 segundos leer un archivo de 1GB que lo colocó en segundo lugar en cuanto a leer archivos más grandes.

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

Probado para funcionar en Java 7, 8, 9. Esto tardó aproximadamente 4,5 segundos en leer en un archivo de prueba de 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);
      }
    }
  }

Puede encontrar la clasificación completa de los 10 métodos de lectura de archivos aquí .

gomisha
fuente
1
Tu guía es increíble :)
Faisal Julaidan
La mayoría de las veces estás cronometrando System.out.print/println()aquí; también está asumiendo que el archivo encajará en la memoria en sus dos primeros casos.
Marqués de Lorne el
Lo suficientemente justo. Tal vez podría haber hecho esos supuestos más explícitos en mi respuesta.
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);
}
Diego Duarte
fuente
99
ten cuidado! usando line.split de esta manera NO se analizará correctamente si un campo contiene una coma y está rodeado de comillas. Esta división ignorará eso y solo separará el campo en fragmentos utilizando la coma interna. HTH, Marcelo.
Marcelo Finki
CSV: archivo de valores separados por comas, por lo tanto, no debe usar comas en un campo csv, a menos que quiera agregar otro campo. Por lo tanto, use dividir para el token de coma en Java cuando analizar un archivo CSV está perfectamente bien
Diego Duarte
77
Diego, esto no es correcto. El único estándar CSV (RFC 4180) dice específicamente "Los campos que contienen saltos de línea (CRLF), comillas dobles y comas deben estar entre comillas dobles".
serg.nechaev
2
Use StandardCharsets.UTF_8para evitar la excepción marcada enCharset.forName("UTF-8")
Aleksandr Dubinsky
2
Gracias "Diego Duarte" por tu comentario; Debo decir que estoy de acuerdo con lo que responde "serg.nechaev". Veo comas incrustadas en archivos csv 'todo el tiempo'. La gente espera que esto sea aceptado. con todo el debido respeto. También muchas gracias a "serg.nechaev". En mi humilde opinión tienes razón. Anima a todos.
Marcelo Finki
13

En Java 8, también hay una alternativa al uso Files.lines(). Si su fuente de entrada no es un archivo, sino algo más abstracto como a Readero an InputStream, puede transmitir las líneas a través del método BufferedReaders lines().

Por ejemplo:

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

llamará processLine()a cada línea de entrada leída por BufferedReader.

Rüdiger Herrmann
fuente
10

Para leer un archivo 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();
        }
    }
}
Ankit Sood
fuente
9

Puedes usar la clase Scanner

Scanner sc=new Scanner(file);
sc.nextLine();
Abhilash
fuente
2
@Tim 'Bomb horriblemente' no es un término que reconozco en CS. Qué quieres decir exactamente?
Marqués de Lorne
Agacharse, ejecutar muy lentamente, muy probablemente se estrelle. Probablemente debería evitar modismos en este sitio;)
Tim
44
@Tim ¿Por qué lo haría?
xehpuk
2
Usar Scannerestá bien, pero esta respuesta no incluye el código completo para usarlo correctamente.
Aleksandr Dubinsky
55
@Tim Este código no "bombardeará horriblemente" ni "empantanará" ni "ejecutará muy lentamente" ni "probablemente se bloqueará". De hecho, tal como está escrito, solo leerá una línea, casi instantáneamente. Puede leer megabytes por segundo de esta manera, aunque BufferedReader.readLine()ciertamente es varias veces más rápido. Si piensa lo contrario, indique sus razones.
Marqués de Lorne
7

Necesitas usar el readLine()método en class BufferedReader. Cree un nuevo objeto de esa clase y opere este método en él y guárdelo en una cadena.

BufferReader Javadoc

Master C
fuente
Parece que el enlace a BufferReaderAPI está roto
Sandeep
6

La forma clara de lograr esto,

Por ejemplo:

Si tienes dataFile.txten tu directorio actual

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 salida como a continuación, ingrese la descripción de la imagen aquí

Rajamohan S
fuente
¿Por qué está más claro? Y no publique imágenes de texto aquí. Publica el texto.
Marqués de Lorne
Publicaste una foto. Es una imagen de texto. Podría haber cortado y pegado el texto directamente en esta página. Nadie dijo nada sobre publicar programas. Publicar imágenes de texto es una pérdida de tiempo, lo que no me importa, y de su ancho de banda, que sí.
Marqués de Lorne
6

Java 9:

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
    stream.forEach(System.out::println);
}
Abdennour TOUMI
fuente
2
Creo que tienes que hacerloSystem.getProperty("os.name").equals("Linux")
SpringLearner
55
¡No compares cadenas con ==!
JonasCz - Restablecer Monica
66
Este es el ejemplo canónico de Java 8, como ya lo han publicado otros. ¿Por qué afirmas que esto es "Java-9"?
Holger
Los archivos mapeados de memoria @Holger que olvidó mencionar pueden ser?
Eugene
para procesarlo línea por línea, puede intentar (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");
}

Esto funciona para mi. Espero que te ayude también.

Dipendra Ghatal
fuente
3

Puede usar transmisiones para hacerlo con mayor precisión:

Files.lines(Paths.get("input.txt")).forEach(s -> stringBuffer.append(s);
puntiagudo
fuente
2
Estoy de acuerdo en que en realidad está bien. Adivina, a la gente no le gusta debido a la extraña elección de StringBuffer (generalmente se prefiere StringBuilder, aunque podría ser un mal nombre para variable). También porque ya se mencionó anteriormente.
Andrii Rubtsov
2

Usualmente hago la rutina de lectura directa:

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) {
        }
    }
}
Binkan Salaryman
fuente
0

Puedes usar este código:

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

    }

}
Usman Yaqoob
fuente
Una explicación estaría en orden.
Peter Mortensen
0

Al usar el paquete org.apache.commons.io , proporcionó más rendimiento, especialmente en el código heredado que usa Java 6 y versiones posteriores.

Java 7 tiene una mejor API con menos manejo de excepciones y métodos más útiles:

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>
mohsen.nour
fuente
0

También puede usar 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();
}
A Kra
fuente
3
FileUtils.readLines(file)Es un método obsoleto. Además, el método invoca IOUtils.readLines, que utiliza un BufferedReader y ArrayList. Este no es un método línea por línea, y ciertamente no es uno que sea práctico para leer varios GB.
vallismortis