Как я могу получить количество строк в файле эффективным способом? [дубликат]



этот вопрос уже есть ответ здесь:



У меня есть большой файл. Она включает в себя примерно 3.000-20.000 линий. Как я могу получить общее количество строк в файл с помощью Java?

645   17  

17 ответов:

BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
int lines = 0;
while (reader.readLine() != null) lines++;
reader.close();

обновление: чтобы ответить на вопрос о производительности, поднятый здесь, я сделал измерение. Во-первых: 20.000 строк слишком мало, чтобы заставить программу работать в течение заметного времени. Я создал текстовый файл с 5 миллионами строк. Это решение (начатое с java без параметров, таких как-server или-XX-options) требовалось около 11 секунд на моем поле. То же самое с wc -l (Unix command-line-инструмент для подсчета строк), 11 секунд. Решение читает каждый символ и смотрит для '\n ' понадобилось 104 секунды, в 9-10 раз больше.

использовать LineNumberReader

что-то вроде

public static int countLines(File aFile) throws IOException {
    LineNumberReader reader = null;
    try {
        reader = new LineNumberReader(new FileReader(aFile));
        while ((reader.readLine()) != null);
        return reader.getLineNumber();
    } catch (Exception ex) {
        return -1;
    } finally { 
        if(reader != null) 
            reader.close();
    }
}

Java 8+ имеет очень хороший и короткий путь, используя NIO:

Path path = Paths.get("./big_file.txt");
long lineCount = Files.lines(path).count();

Я нашел какое-то решение для этого, это может быть полезно для вас

Ниже приведен фрагмент кода, считай нет.строк из файла.

  File file = new File("/mnt/sdcard/abc.txt");
  LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(file));
  lineNumberReader.skip(Long.MAX_VALUE);
  int lines = lineNumberReader.getLineNumber();
  lineNumberReader.close();

прочитайте файл и подсчитайте количество символов новой строки. Простой способ, чтобы прочитать файл в Java, по одной строке за раз, это java.утиль.Сканер класса.

Это примерно так же эффективно, как он может получить, буферизованное двоичное чтение, без преобразования строк,

FileInputStream stream = new FileInputStream("/tmp/test.txt");
byte[] buffer = new byte[8192];
int count = 0;
int n;
while ((n = stream.read(buffer)) > 0) {
    for (int i = 0; i < n; i++) {
        if (buffer[i] == '\n') count++;
    }
}
stream.close();
System.out.println("Number of lines: " + count);

вам нужно точное количество линий или только его приближение? Мне случается обрабатывать большие файлы параллельно, и часто мне не нужно знать точное количество строк - затем я возвращаюсь к выборке. Разделите файл на десять кусков по 1 МБ и подсчитайте строки в каждом куске, затем умножьте его на 10, и вы получите довольно хорошее приближение количества строк.

все предыдущие ответы предлагают прочитать весь файл и подсчитать количество новых строк, которые вы найдете при этом. Вы прокомментировали некоторые как "не эффективно", но это единственный способ, которым вы можете это сделать. "Линия" - это не что иное, как простой символ внутри файла. И чтобы подсчитать этот символ, вы должны взглянуть на каждый символ в файле.

прости, но у тебя нет выбора. : -)

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

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

быстро и грязно, но это делает работу:

import java.io.*;

public class Counter {

    public final static void main(String[] args) throws IOException {
        if (args.length > 0) {
            File file = new File(args[0]);
            System.out.println(countLines(file));
        }
    }

    public final static int countLines(File file) throws IOException {
        ProcessBuilder builder = new ProcessBuilder("wc", "-l", file.getAbsolutePath());
        Process process = builder.start();
        InputStream in = process.getInputStream();
        LineNumberReader reader = new LineNumberReader(new InputStreamReader(in));
        String line = reader.readLine();
        if (line != null) {
            return Integer.parseInt(line.trim().split(" ")[0]);
        } else {
            return -1;
        }
    }

}

это решение примерно на 3,6× быстрее, чем самый рейтинговый ответ при тестировании на файле с 13,8 миллиона строк. Он просто считывает байты в буфер и считает \n символы. Вы можете играть с размером буфера, но на моей машине все, что выше 8 КБ, не делает код быстрее.

private int countLines(File file) throws IOException {
    int lines = 0;

    FileInputStream fis = new FileInputStream(file);
    byte[] buffer = new byte[BUFFER_SIZE]; // BUFFER_SIZE = 8 * 1024
    int read;

    while ((read = fis.read(buffer)) != -1) {
        for (int i = 0; i < read; i++) {
            if (buffer[i] == '\n') lines++;
        }
    }

    fis.close();

    return lines;
}

попробуйте команду unix "wc". Я не имею в виду использовать его, я имею в виду скачать источник и посмотреть, как они это делают. Это, вероятно, в c, но вы можете легко перенести поведение на java. Проблема с созданием собственного заключается в том, чтобы учитывать конечную проблему cr/lf.

старый пост, но у меня есть решение, которое может быть полезно для следующих людей. Почему бы просто не использовать длину файла, чтобы узнать, что такое прогрессия? Конечно, строки должны быть почти одинакового размера, но это очень хорошо работает для больших файлов:

public static void main(String[] args) throws IOException {
    File file = new File("yourfilehere");
    double fileSize = file.length();
    System.out.println("=======> File size = " + fileSize);
    InputStream inputStream = new FileInputStream(file);
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "iso-8859-1");
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
    int totalRead = 0;
    try {
        while (bufferedReader.ready()) {
            String line = bufferedReader.readLine();
            // LINE PROCESSING HERE
            totalRead += line.length() + 1; // we add +1 byte for the newline char.
            System.out.println("Progress ===> " + ((totalRead / fileSize) * 100) + " %");
        }
    } finally {
        bufferedReader.close();
    }
}

Это позволяет увидеть прогрессию, не делая никакого полного чтения на файл. Я знаю, что это зависит от многих элементов, но я надеюсь, что это будет полезно :).

[издание] Вот версия с расчетным временем. Я поставил некоторые SYSO, чтобы показать прогресс и оценка. Я вижу, что у вас есть хорошие ошибки оценки времени после того, как вы обработали достаточно линии (я пытаюсь с линиями 10M, и после 1% лечения оценка времени была точной на 95%). Я знаю, некоторые значения должны быть установлены в переменной. Этот код быстро написан, но был полезен для меня. Надеюсь, что это будет и для вас тоже :).

long startProcessLine = System.currentTimeMillis();
    int totalRead = 0;
    long progressTime = 0;
    double percent = 0;
    int i = 0;
    int j = 0;
    int fullEstimation = 0;
    try {
        while (bufferedReader.ready()) {
            String line = bufferedReader.readLine();
            totalRead += line.length() + 1;
            progressTime = System.currentTimeMillis() - startProcessLine;
            percent = (double) totalRead / fileSize * 100;
            if ((percent > 1) && i % 10000 == 0) {
                int estimation = (int) ((progressTime / percent) * (100 - percent));
                fullEstimation += progressTime + estimation;
                j++;
                System.out.print("Progress ===> " + percent + " %");
                System.out.print(" - current progress : " + (progressTime) + " milliseconds");
                System.out.print(" - Will be finished in ===> " + estimation + " milliseconds");
                System.out.println(" - estimated full time => " + (progressTime + estimation));
            }
            i++;
        }
    } finally {
        bufferedReader.close();
    }
    System.out.println("Ended in " + (progressTime) + " seconds");
    System.out.println("Estimative average ===> " + (fullEstimation / j));
    System.out.println("Difference: " + ((((double) 100 / (double) progressTime)) * (progressTime - (fullEstimation / j))) + "%");

Не стесняйтесь улучшать этот код, если вы считаете, что это хорошее решение.

читать файл строка за строкой и увеличить счетчик для каждой строки, пока вы не прочитали весь файл.

в моих тестах другие ответы занимают ~150-300 мс в линейном файле 118.5 k. Следующее занимает 1 мс, но является только приблизительным (сообщает 117k строк) и зависит от того, что каждая строка имеет одинаковый размер.

private static void countSize(File file) {
  long fileLength = file.length();
  BufferedReader reader = null;
  try {
    reader = new BufferedReader(new FileReader(file));
    //Skip header as it is of different size
    reader.readLine();
    String text = reader.readLine();
    int lineLength = text.length();
    long lines = fileLength / lineLength;
    System.out.println(lines);
  } catch(IOException e) {
    e.printStackTrace();
  } finally {
    if(reader != null) {
      try {
        reader.close();
      } catch(IOException e) {
        //no-op
      }
    }
  }
}

вероятно, самым быстрым решением в чистой Java было бы прочитать файл в виде байтов, используя канал NIO в большой ByteBuffer. Затем, используя свои знания о схеме (схемах) кодирования файлов, подсчитайте кодированные CR и / или NL байты в соответствии с соответствующим соглашением о разделителе строк.

ключ к максимизации пропускной способности будет:

  • убедитесь, что Вы читаете файл большими кусками,
  • избегайте копирования байтов из одного буфера в другой,
  • избегайте копирования / преобразования байтов в символы, и
  • избежать выделения объектов для представления строк файла.

реальный код слишком сложен для меня, чтобы писать на лету. Кроме того, ОП не просит самого быстрого решения.

буферизованный читатель является излишним

Reader r = new FileReader("f.txt");

int count = 0;
int nextchar = 0;
while (nextchar != -1){
        nextchar = r.read();
        if (nextchar == Character.getNumericValue('\n') ){
            count++;
        }
    }

Мой поиск простого примера создал один, который на самом деле довольно беден. повторный вызов read() для одного символа является менее оптимальным. смотрите здесь для примеров и измерений.

Comments

    Ничего не найдено.