Вопрос-ответ

Number of lines in a file in Java

Количество строк в файле на Java

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

Мне было интересно, есть ли более разумный способ сделать это

Переведено автоматически
Ответ 1

Это самая быстрая версия, которую я нашел на данный момент, примерно в 6 раз быстрее, чем readLines . В файле журнала размером 150 МБ это занимает 0,35 секунды по сравнению с 2,40 секундами при использовании readLines(). Просто ради интереса, команда wc -l в Linux занимает 0,15 секунды.

public static int countLinesOld(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean empty = true;
while ((readChars = is.read(c)) != -1) {
empty = false;
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
}
return (count == 0 && !empty) ? 1 : count;
} finally {
is.close();
}
}

РЕДАКТИРОВАТЬ, 9 1/2 лет спустя: у меня практически нет опыта работы с Java, но в любом случае я попытался сравнить этот код с LineNumberReader решением ниже, поскольку меня беспокоило, что этого никто не делал. Кажется, что мое решение работает быстрее, особенно для больших файлов. Хотя, кажется, требуется несколько запусков, пока оптимизатор не выполнит достойную работу. Я немного поиграл с кодом и создал новую версию, которая неизменно работает быстрее:

public static int countLinesNew(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];

int readChars = is.read(c);
if (readChars == -1) {
// bail out if nothing to read
return 0;
}

// make it easy for the optimizer to tune this loop
int count = 0;
while (readChars == 1024) {
for (int i=0; i<1024;) {
if (c[i++] == '\n') {
++count;
}
}
readChars = is.read(c);
}

// count remaining characters
while (readChars != -1) {
for (int i=0; i<readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
readChars = is.read(c);
}

return count == 0 ? 1 : count;
} finally {
is.close();
}
}

Результаты теста для текстового файла объемом 1,3 ГБ по оси y в секундах. Я выполнил 100 запусков с одним и тем же файлом и измерял каждый запуск с помощью System.nanoTime(). Вы можете видеть, что countLinesOld имеет несколько отклонений, а countLinesNew не имеет ни одного, и хотя это лишь немного быстрее, разница статистически значима. LineNumberReader явно медленнее.

График бенчмарка

Ответ 2

Я реализовал другое решение проблемы, я нашел его более эффективным при подсчете строк:

try
(
FileReader input = new FileReader("input.txt");
LineNumberReader count = new LineNumberReader(input);
)
{
while (count.skip(Long.MAX_VALUE) > 0)
{
// Loop just in case the file is > Long.MAX_VALUE or skip() decides to not read the entire file
}

result = count.getLineNumber() + 1; // +1 because line index starts at 0
}
Ответ 3

Принятый ответ содержит ошибку off by one для многострочных файлов, которые не заканчиваются символом новой строки. Однострочный файл, заканчивающийся без перевода строки, вернет 1, но двухстрочный файл, заканчивающийся без перевода строки, тоже вернет 1. Вот реализация принятого решения, которое исправляет это. Проверки endsWithoutNewLine являются расточительными для всего, кроме окончательного чтения, но должны быть тривиальными по времени по сравнению с общей функцией.

public int count(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean endsWithoutNewLine = false;
while ((readChars = is.read(c)) != -1) {
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n')
++count;
}
endsWithoutNewLine = (c[readChars - 1] != '\n');
}
if(endsWithoutNewLine) {
++count;
}
return count;
} finally {
is.close();
}
}
Ответ 4

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

try (Stream<String> lines = Files.lines(path, Charset.defaultCharset())) {
long numOfLines = lines.count();
...
}
java