Як я можу прочитати великий текстовий файл за рядком за допомогою Java?


846

Мені потрібно прочитати великий текстовий файл розміром близько 5-6 ГБ за рядком за допомогою Java.

Як я можу це швидко зробити?


69
@kamaci та ін. ін. Це питання не слід позначати як дублікат. "Швидке читання останнього рядка" не є альтернативою, і його дискусійним чи є "Найшвидший спосіб читати текстовий файл за рядком". Найшвидший спосіб зробити щось - це не обов'язково звичайний спосіб. Крім того, відповіді нижче включають код, найрелевантнішої альтернативи, яку ви перераховуєте, немає. Це питання корисне. Наразі це найпопулярніший результат пошуку Google для "java read file line by line". Нарешті, його відміняється, щоб досягти переповнення стека та встановити, що 1 на кожне 2 питання позначено для усунення.
Патрік Каллен

5
Ось порівняння швидкості для шести можливих реалізацій.
Serg M Десять

4
Подія, хоч я читав коментарі, стверджуючи, що тісна політика SO відсутня, тому наполегливо зберігається. Це така вузька думка розробника, щоб хотіти уникнути зайвих витрат за будь-яку ціну! Просто хай буде! Крем підніметься до верху, і sh * t опуститься на дно просто добре. Навіть незважаючи на те, що питання, можливо, було задано і раніше (яке питання немає ??), це не означає, що нове запитання може не мати змоги краще його сформулювати, отримати кращі відповіді, вищу позицію в пошукових системах тощо. Цікаво, що це питання зараз "захищене" ....
Штійн де Вітт

3
Неймовірно, як питання ставляться як дублікати, просто читаючи заголовок.
Лука

Відповіді:


1063

Загальна модель - це використовувати

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

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

EDIT: Менш розповсюджена модель, яка дозволяє уникнути сфери 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);
}

ПРИМІТКА: Ви повинні розмістити Потік у блоці спробу використання ресурсів, щоб переконатися, що метод #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())Btw, в 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 Gig, і неможливо прочитати файл, використовуючи вашу відповідь!
Aboozar Rajabi

3
@AboozarRajabi Звичайно, можна. Цей код може читати будь-який текстовий файл.
користувач207421

10
Захищений за низьку якість посилання. Існує абсолютно безглуздо DataInputStream, і неправильний потік закритий. Нічого поганого в навчальному посібнику Java, і немає необхідності цитувати довільні сторонні Інтернет-сміття, як це.
користувач207421

1
Я б закинув коментарі, у вас є 4 рядки 100% зайвих коментарів для 6 рядків коду.
Буффало

97

Після виходу 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);
}

1
Використовуйте StandardCharsets.UTF_8, використовуйте Stream<String>для стислість, уникайте використання, forEach()особливо forEachOrdered()якщо немає причини.
Олександр Дубінський

2
Чому варто уникати для кожного ()? Це погано?
steventrouble

Якщо я нам forEach замість forEachOrряд, рядки можуть бути надруковані не в порядку, чи не так?
msayag

2
@steventrouble Погляньте: stackoverflow.com/questions/16635398 / ... Це не погано , якщо ви пройдете коротку посилання на функцію , як forEach(this::process), але він отримує потворним , якщо ви пишете блоки коду , як лямбда всередині forEach().
Олександр Дубінський

2
@msayag, Ти маєш рацію, потрібно forEachOrderedдля того, щоб виконати замовлення. Будьте в курсі, що ви не зможете паралелізувати потік у такому випадку, хоча я виявив, що паралелізація не включається, якщо файл не має тисяч рядків.
Олександр Дубінський

38

Ось зразок з повною обробкою помилок та підтримкою специфікації шаблонів для попередньої Java 7. Для Java 7 можна використовувати синтаксис "пробний ресурс", що робить код чистішим.

Якщо ви просто хочете встановити схему за замовчуванням, ви можете пропустити 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літерального рядка з читанням великого текстового файлу?
користувач207421

абсолютно марно закривається. Для закриття кожного потоку немає жодної причини. Якщо ви закриєте будь-який з цих потоків, ви автоматично закриєте всі інші потоки ...
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<String>)).
Стефан

Як я можу пропустити перший рядок цим методом?
qed

2
@qedfor(String line : (Iterable<String>) lines.skip(1)::iterator)
Олександр Дубінський

1
Якщо ви не збираєтеся насправді використовувати Streamфункції, використовуючи Files.newBufferedReaderзамість Files.linesі повторюваного виклику , readLine()поки nullзамість того , щоб використовувати конструкції , як , (Iterable<String>) lines::iteratorздається, набагато простіше ...
Хольгер

Для чого ви використовуєте :: у рядках :: ітератор? Єдине використання, яке я знаю для :: - це впакування імені методу у функцію лямбда. Параметр для для циклу після: повинен бути змінним, поки ви отримуєте метод лямбда, використовуючи ::
Trismegistos

19

Що ви можете зробити, це сканувати весь текст за допомогою Scanner і пройти текст за рядком. Звичайно, вам слід імпортувати наступне:

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 - текст типу файлу.


Чи не повинен замість цього декларація методу виглядати так: ´публічна статична void readText кидає FileNotFoundException () {´ Like: ´public statična void readText () кидає FileNotFoundException {´
Ketcomp

Це значно повільніше BufferedReader.readLine(), і він попросив найбільш ефективний метод.
користувач207421

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 методів читання файлів ви можете знайти тут .


1
Ваш путівник дивовижний :)
Файсал Джулайдан

Ви в основному проводите System.out.print/println()тут; ви також припускаєте, що файл поміститься в пам'яті у ваших перших двох випадках.
користувач207421

Справедливо. Можливо, я міг би зробити ці припущення більш чіткими у своїй відповіді.
гоміша

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), подвійні лапки та коми повинні бути укладені у подвійні лапки".
серг.нечаєв

2
Використовуйте, StandardCharsets.UTF_8щоб уникнути перевіреного винятку вCharset.forName("UTF-8")
Олександр Дубінський,

2
Дякую "Дієго Дуарте" за ваш коментар; я повинен сказати, що я згоден з тим, що відповідає "serg.nechaev". Я бачу коми, вбудовані у файли CSV "весь час". Люди очікують, що це буде прийнято. з усією повагою. також велика подяка "serg.nechaev". ІМХО ви праві. Веселіть усіх.
Марсело Фінкі

13

У Java 8 також є альтернатива використанню Files.lines(). Якщо ваше джерело введення не є файлом, а чимось більш абстрактним, як-от 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();

2
@Tim "Бомба жахливо" - це не термін, який я визнаю в CS. Що саме ти маєш на увазі?
користувач207421

Бог вниз, виконайте дуже повільно, швидше за все, крах. Напевно, я повинен уникати ідіом на цьому сайті;)
Тім

4
@Tim Навіщо це робити?
xehpuk

2
Використовувати Scannerце чудово, але ця відповідь не включає повний код, щоб правильно його використовувати.
Олександр Дубінський

5
@Tim Цей код не буде ні «бомбити жахливо», ні «забитися», ні «виконувати дуже повільно», ні «швидше за все, аварії». Власне кажучи, як написано, він буде читати лише один рядок, майже мимоволі. Ви можете читати мегабайти в секунду таким чином, хоча BufferedReader.readLine()це, звичайно, в кілька разів швидше. Якщо ви думаєте про інше, будь ласка, вкажіть свої причини.
user207421

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

Вихід, як показано нижче, введіть тут опис зображення


Чому це зрозуміліше? І не публікуйте тут зображень тексту. Опублікуйте текст.
користувач207421

Ви опублікували фотографію. Це зображення тексту. Ви могли вирізати і вставити текст прямо на цю сторінку. Ніхто нічого не сказав про розміщення програм. Опублікування зображень з тексту - це марна трата вашого часу, яка мене не хвилює, і пропускну здатність вашої роботи, яку я роблю.
користувач207421

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"?
Холгер

Можливо, файли, зібрані в пам'яті @Holger, які він забув згадати?
Євген

обробляти його рядок за рядком ви можете спробувати (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>

0

Ви також можете використовувати IO Apache Commons :

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.