Как я могу читать большой текстовый файл построчно, используя Java?

848

Мне нужно читать большой текстовый файл размером около 5-6 ГБ построчно, используя Java.

Как я могу сделать это быстро?

Манодж Сингх
источник
69
@kamaci et. и др. Этот вопрос не должен быть помечен как дубликат. «Быстро прочитать последнюю строчку» не является альтернативой, и ее спорно ли «быстрый способ чтения текстового файла строка за строкой» есть. Самый быстрый способ сделать что-то не обязательно является обычным способом. Кроме того, ответы ниже включают код, а наиболее подходящая альтернатива, которую вы перечисляете, - нет. Этот вопрос полезен. В настоящее время это лучший результат поиска в Google по запросу "java read file by line". И, наконец, его устранение приводит к переполнению стека и обнаружению, что 1 в каждом 2 вопросе помечается для удаления.
Патрик Каллен
5
Вот сравнение скорости для шести возможных реализаций.
Серж М Тен
4
Несмотря на то, что я читал комментарии, утверждая, что близкая политика SO - отстой, SO остается в этом. Это такая ограниченная перспектива для разработчиков, чтобы хотеть избежать избыточности любой ценой! Просто пусть это будет! Крем поднимется до самого верха, а дерьмо само по себе опустится до самого дна. Несмотря на то, что вопрос, возможно, задавался ранее (какой вопрос не является ??), это не означает, что новый вопрос не сможет лучше его сформулировать, получить лучшие ответы, повысить рейтинг в поисковых системах и т. Д. Интересно, что это вопрос теперь «защищен» ....
Stijn de Witt
3
Просто невероятно, как вопросы помечаются как дубликаты, просто читая заголовок.
Люк

Ответы:

1064

Распространенным примером является использование

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

Вы можете читать данные быстрее, если предположите, что кодировка символов отсутствует. например, ASCII-7, но это не будет иметь большого значения. Весьма вероятно, что то, что вы будете делать с данными, займет гораздо больше времени.

РЕДАКТИРОВАТЬ: менее распространенный шаблон, который позволяет избежать lineутечки.

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

ОБНОВЛЕНИЕ: в Java 8 вы можете сделать

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

ПРИМЕЧАНИЕ. Вы должны поместить Stream в блок try-with-resource, чтобы убедиться, что для него вызывается метод #close, иначе основной дескриптор файла никогда не будет закрыт, пока GC не сделает это намного позже.

Питер Лори
источник
6
Как выглядит этот шаблон при правильной обработке исключений? Я отмечаю, что br.close () генерирует IOException, что кажется удивительным - что может произойти при закрытии файла, открытого для чтения, в любом случае? Конструктор FileReader может вызвать исключение FileNotFound.
MikeB
3
Если у меня есть файл 200 МБ, и он может читать со скоростью 90 МБ / с, то я ожидаю, что это займет ~ 3 с? Похоже, мое занятие заняло несколько минут с этим «медленным» способом чтения Я на SSD, поэтому скорость чтения не должна быть проблемой?
Jiew Meng
4
@JiewMeng Так что я подозреваю, что что-то еще, что вы делаете, требует времени. Можете ли вы попробовать просто прочитать строки файла и ничего больше.
Питер Лори
44
Почему бы и нет for(String line = br.readLine(); line != null; line = br.readLine()), в Java 8 вы можете сделать, try( Stream<String> lines = Files.lines(...) ){ for( String line : (Iterable<String>) lines::iterator ) { ... } }что трудно не ненавидеть.
Александр Дубинский
26
@AleksandrDubinsky Проблема с замыканиями в Java 8 заключается в том, что она очень легко делает код более сложным для чтения (а также более медленным). Я вижу, что многие разработчики злоупотребляют им, потому что это «круто».
Питер Лори
155

Посмотрите на этот блог:

Размер буфера может быть указан, или размер по умолчанию может быть использован. Значение по умолчанию достаточно велико для большинства целей.

// 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
Мой файл 1,5 гигабайт, и невозможно прочитать файл, используя ваш ответ!
Aboozar Раджаби
3
@AboozarRajabi Конечно, это возможно. Этот код может читать любой текстовый файл.
Маркиз Лорн
10
Отказался за некачественную ссылку. Тут совершенно бессмысленно DataInputStream, а не тот поток закрыт. Ничего плохого в Java Tutorial, и нет необходимости цитировать произвольный сторонний мусор в Интернете, как этот.
Маркиз Лорн
1
Я бы отказался от комментариев, у вас есть 4 строки 100% избыточных комментариев для 6 строк кода.
Буффало,
98

После выхода Java 8 (март 2014 года) вы сможете использовать потоки:

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

Печать всех строк в файле:

try (Stream<String> lines = Files.lines(file, Charset.defaultCharset())) {
  lines.forEachOrdered(System.out::println);
}
msayag
источник
1
Используйте StandardCharsets.UTF_8, используйте Stream<String>для краткости, и избегайте использования, forEach()и особенно forEachOrdered()если нет причины.
Александр Дубинский
2
Зачем избегать forEach ()? Это плохо?
Steventrouble
Если бы я использовал forEach вместо forEachOrdered, строки могли бы быть напечатаны не по порядку, не так ли?
msayag
2
@steventrouble Взгляните на: stackoverflow.com/questions/16635398/… Это неплохо, если вы передаете краткую ссылку на функцию наподобие forEach(this::process), но становится уродливым, если вы пишете блоки кода как лямбды внутри forEach().
Александр Дубинский
2
@msayag, ты прав, нужно forEachOrderedдля того чтобы выполнить по порядку. Имейте в виду, что в этом случае вы не сможете распараллелить поток, хотя я обнаружил, что распараллеливание не включается, если в файле нет тысяч строк.
Александр Дубинский
38

Вот пример с полной обработкой ошибок и поддержкой спецификации кодировки для pre-Java 7. В Java 7 вы можете использовать синтаксис try-with-resources, который делает код чище.

Если вы просто хотите использовать кодировку по умолчанию, вы можете пропустить InputStream и использовать 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 */ } }
}

Вот Groovy-версия с полной обработкой ошибок:

File f = new File("textfile.txt");
f.withReader("UTF-8") { br ->
    br.eachLine { line ->
        println line;
    }
}
Темная звезда
источник
1
Как ByteArrayInputStreamпоступает строковый литерал с чтением большого текстового файла?
Маркиз Лорн
абсолютно бесполезно закрывается. Нет нужды закрывать каждый поток. Если вы закроете любой из этих потоков, вы автоматически закроете все остальные потоки ...
Enerccio
21

В Java 8 вы можете сделать:

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

Некоторые примечания: поток, возвращаемый Files.lines(в отличие от большинства потоков), должен быть закрыт. По причинам, указанным здесь, я избегаю использования forEach(). Странный код(Iterable<String>) lines::iterator переводит поток в Iterable.

Александр Дубинский
источник
Отказ от реализации Iterableэтого кода определенно уродлив, хотя и полезен. Это нуждается в броске (то есть (Iterable<String>)), чтобы работать.
Стефан
Как я могу пропустить первую строку с этим методом?
Qed
2
@qedfor(String line : (Iterable<String>) lines.skip(1)::iterator)
Александр Дубинский
1
Если вы не собираетесь на самом деле использовать Streamфункции, используя Files.newBufferedReaderвместо Files.linesи повторяющегося вызова , readLine()пока nullвместо того , чтобы использовать конструкции , как , (Iterable<String>) lines::iteratorкажется, гораздо проще ...
Хольгер
Почему вы используете :: in lines :: iterator? Единственное использование, которое я знаю для :: - это упаковывать имя метода в лямбда-функцию. В течение параметра цикла после того, как : должен быть переменной , а вы получите некоторый метод лямбда с помощью ::
Трисмегиста
19

Что вы можете сделать, это отсканировать весь текст с помощью сканера и проходить текст построчно. Конечно, вы должны импортировать следующее:

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

Сканер в основном сканирует весь текст. Цикл while используется для перемещения по всему тексту.

.hasNextLine()Функция булева , которая возвращает истину , если есть еще несколько строк в тексте. .nextLine()Функция дает Вам всю строку в виде строки , которые вы можете использовать, как вы хотите. ПытатьсяSystem.out.println(line) напечатать текст.

Примечание: .txt - это текст типа файла.

iskandarchacra
источник
Должно ли объявление метода выглядеть вместо этого: «public static void readText throws FileNotFoundException () {´ Like:« public static void readText () throws FileNotFoundException {´
Ketcomp
Это значительно медленнее, чем он BufferedReader.readLine(), и он попросил самый эффективный метод.
Маркиз Лорн
18

FileReader не позволит вам указать кодировку, используйте InputStreamReaderвместо этого, если вам нужно указать ее:

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

Если вы импортировали этот файл из Windows, он может иметь кодировку ANSI (Cp1252), поэтому вам необходимо указать кодировку.

живи любя
источник
17

Я задокументировал и протестировал 10 различных способов чтения файлов на Java, а затем сопоставил их друг с другом, заставив их читать в тестовых файлах от 1 КБ до 1 ГБ. Вот самые быстрые 3 метода чтения файлов для чтения тестового файла объемом 1 ГБ.

Обратите внимание, что при выполнении тестов производительности я ничего не выводил на консоль, поскольку это действительно замедляло бы тестирование. Я просто хотел проверить скорость чтения.

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

Протестировано в Java 7, 8, 9. В целом это был самый быстрый метод. Чтение файла объемом 1 ГБ всегда было менее 1 секунды.

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

Это было успешно протестировано в Java 8 и 9, но не будет работать в Java 7 из-за отсутствия поддержки лямбда-выражений. Чтение файла размером 1 ГБ заняло около 3,5 секунд, что ставит его на второе место после чтения больших файлов.

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

Проверено на работу в Java 7, 8, 9. Для считывания тестового файла объемом 1 ГБ потребовалось около 4,5 секунд.

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

Вы можете найти полный рейтинг всех 10 методов чтения файлов здесь .

gomisha
источник
1
Ваш гид потрясающий :)
Фейсал Джулайдан
Вы в основном рассчитываете System.out.print/println()здесь; вы также предполагаете, что файл поместится в память в первых двух случаях.
Маркиз Лорн
Справедливо. Возможно, я мог бы сделать эти предположения более четкими в своем ответе.
Гомиша
16

В 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
быть в курсе! использование line.split таким способом НЕ будет правильно проанализировано, если поле содержит запятую и окружено кавычками. Это разделение игнорирует это и просто разделяет поле на куски, используя внутреннюю запятую. HTH, Марсело.
Марсело Финки,
CSV: файл значений, разделенных запятыми, поэтому вам не следует использовать запятую в поле csv, если только вы не хотите добавить другое поле. Таким образом, использовать разбивку для токена запятой в java при разборе CSV-файла совершенно нормально и правильно
Диего Дуарте
7
Диего, это не правильно. Единственный стандарт CSV (RFC 4180) специально гласит: «Поля, содержащие разрывы строк (CRLF), двойные кавычки и запятые, должны быть заключены в двойные кавычки».
serg.nechaev
2
Используйте, StandardCharsets.UTF_8чтобы избежать проверенного исключения вCharset.forName("UTF-8")
Александр Дубинский
2
Спасибо "Диего Дуарте" за ваш комментарий; Должен сказать, я согласен с тем, что отвечает «serg.nechaev». Я вижу запятые, встроенные в CSV-файлы "все время". Люди ожидают, что это будет принято. при всем моем уважении. также большое спасибо "серг.нечаев". ИМХО ты прав. Приветствую Всех.
Марсело Финки
13

В Java 8 также есть альтернатива использованию Files.lines(). Если ваш входной источник не файл, а что-то более абстрактное, например a Readerили an InputStream, вы можете передавать строки с помощью метода BufferedReaders lines().

Например:

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

будет вызывать processLine()для каждой строки ввода, прочитанной BufferedReader.

Рюдигер Херрманн
источник
10

Для чтения файла с 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

Вы можете использовать класс сканера

Scanner sc=new Scanner(file);
sc.nextLine();
Abhilash
источник
2
@Tim «Бомба ужасно» - это не термин, который я узнаю в CS. Что именно ты имеешь ввиду?
маркиз Лорн
Погрузить, выполнить очень медленно, скорее всего, сбой. Я, вероятно, должен избегать идиом на этом сайте;)
Тим
4
@ Тим почему бы это сделать?
xehpuk
2
Использование Scannerэто хорошо, но этот ответ не включает полный код, чтобы использовать его правильно.
Александр Дубинский
5
@Tim Этот код не будет ни «ужасно бомбить», ни «сваливать», ни «выполнять очень медленно», ни «скорее всего, падать». На самом деле, как написано, он будет читать только одну строку, почти мгновенно. Вы можете читать мегабайты в секунду таким образом, хотя BufferedReader.readLine(), безусловно, в несколько раз быстрее. Если вы думаете иначе, пожалуйста, укажите ваши причины.
Маркиз Лорн
7

Вам нужно использовать readLine()метод в class BufferedReader. Создайте новый объект из этого класса, примените этот метод к нему и сохраните его в строку.

BufferReader Javadoc

Мастер С
источник
Кажется, что ссылка на BufferReaderAPI не работает
Sandeep
6

Четкий способ достичь этого,

Например:

Если у вас есть dataFile.txtв вашем текущем каталоге

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

Вывод, как показано ниже, введите описание изображения здесь

Раджамохан С
источник
Почему это понятнее? И не размещайте фотографии текста здесь. Опубликовать текст
Маркиз Лорн
Вы разместили картинку. Это изображение текста. Вы могли бы вырезать и вставить текст прямо на эту страницу. Никто ничего не сказал о размещении программ. Публикация фотографий с текстами - это пустая трата вашего времени, которое мне безразлично, и пропускная способность, которую я делаю.
Маркиз Лорн
6

Java 9:

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
    stream.forEach(System.out::println);
}
Абденнур ТУМИ
источник
2
Я думаю, что вы должныSystem.getProperty("os.name").equals("Linux")
SpringLearner
5
Не сравнивайте строки с ==!
JonasCz - Восстановить Монику
6
Это канонический пример Java 8, уже опубликованный другими. Почему вы утверждаете, что это «Java-9»?
Хольгер
@ Хольгер отображал в памяти файлы, которые он забыл упомянуть, может быть?
Евгений,
чтобы обрабатывать его построчно, вы можете попытаться (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");
}

Меня устраивает. Надеюсь, это вам тоже поможет.

Дипендра Гхатал
источник
3

Вы можете использовать потоки, чтобы сделать это более точно:

Files.lines(Paths.get("input.txt")).forEach(s -> stringBuffer.append(s);
Паучок
источник
2
Я согласен, что это на самом деле хорошо. Конечно, людям это не нравится из-за странного выбора StringBuffer (обычно предпочитается StringBuilder, хотя это может быть просто неправильное имя для переменной). Кроме того, потому что это уже упоминалось выше.
Андрей Рубцов
2

Я обычно делаю рутину чтения просто:

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

Вы можете использовать этот код:

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

    }

}
Усман Якуб
источник
Объяснение будет в порядке.
Питер Мортенсен
0

Использование пакета org.apache.commons.io позволило повысить производительность, особенно в устаревшем коде, который использует Java 6 и ниже.

Java 7 имеет лучший API с меньшим количеством обработок исключений и более полезными методами:

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

специалист

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
mohsen.nour
источник
0

Вы также можете использовать 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)является устаревшим методом. Кроме того, метод вызывает IOUtils.readLines, который использует BufferedReader и ArrayList. Это не построчный метод, и, конечно, он не подходит для чтения нескольких ГБ.
vallismortis