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

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

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

16 Solutions collect form web for “Количество строк в файле в Java”

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

public static int countLines(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(); } } 

EDIT, 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) { System.out.println(readChars); for (int i=0; i 

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

Контрольный план

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

 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 } 

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

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

С помощью java-8 вы можете использовать streamи:

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

Ответ с методом count () выше дал мне строки ошибок, если в конце файла не было новой строки – он не смог подсчитать последнюю строку в файле.

Этот метод работает лучше для меня:

 public int countLines(String filename) throws IOException { LineNumberReader reader = new LineNumberReader(new FileReader(filename)); int cnt = 0; String lineRead = ""; while ((lineRead = reader.readLine()) != null) {} cnt = reader.getLineNumber(); reader.close(); return cnt; } 

Я знаю, что это старый вопрос, но принятое решение не совсем соответствовало тому, что мне было нужно. Таким образом, я уточнил, что он принимает различные терминаторы строк (а не только фиды строк) и использует заданную кодировку символов (а не ISO-8859- n ). Все в одном методе (рефакторинг, если необходимо):

 public static long getLinesCount(String fileName, String encodingName) throws IOException { long linesCount = 0; File file = new File(fileName); FileInputStream fileIn = new FileInputStream(file); try { Charset encoding = Charset.forName(encodingName); Reader fileReader = new InputStreamReader(fileIn, encoding); int bufferSize = 4096; Reader reader = new BufferedReader(fileReader, bufferSize); char[] buffer = new char[bufferSize]; int prevChar = -1; int readCount = reader.read(buffer); while (readCount != -1) { for (int i = 0; i < readCount; i++) { int nextChar = buffer[i]; switch (nextChar) { case '\r': { // The current line is terminated by a carriage return or by a carriage return immediately followed by a line feed. linesCount++; break; } case '\n': { if (prevChar == '\r') { // The current line is terminated by a carriage return immediately followed by a line feed. // The line has already been counted. } else { // The current line is terminated by a line feed. linesCount++; } break; } } prevChar = nextChar; } readCount = reader.read(buffer); } if (prevCh != -1) { switch (prevCh) { case '\r': case '\n': { // The last line is terminated by a line terminator. // The last line has already been counted. break; } default: { // The last line is terminated by end-of-file. linesCount++; } } } } finally { fileIn.close(); } return linesCount; } 

Это решение сопоставимо по скорости с принятым решением, примерно на 4% медленнее в моих тестах (хотя временные тесты на Java, как известно, ненадежны).

 /** * Count file rows. * * @param file file * @return file row count * @throws IOException */ public static long getLineCount(File file) throws IOException { try (Stream lines = Files.lines(file.toPath())) { return lines.count(); } } 

Протестировано на JDK8_u31. Но на самом деле производительность медленная по сравнению с этим методом:

 /** * Count file rows. * * @param file file * @return file row count * @throws IOException */ public static long getLineCount(File file) throws IOException { try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(file), 1024)) { byte[] c = new byte[1024]; boolean empty = true, lastEmpty = false; long count = 0; int read; while ((read = is.read(c)) != -1) { for (int i = 0; i < read; i++) { if (c[i] == '\n') { count++; lastEmpty = true; } else if (lastEmpty) { lastEmpty = false; } } empty = false; } if (!empty) { if (count == 0) { count = 1; } else if (!lastEmpty) { count++; } } return count; } } 

Протестировано и очень быстро.

Я пришел к выводу, что wc -l : метод подсчета новых строк хорош, но возвращает неинтуитивные результаты в файлах, где последняя строка не заканчивается символом новой строки.

И решение @ er.vikas, основанное на LineNumberReader, но добавляющее один к счету строк, возвращают неинтуитивные результаты в файлах, где последняя строка заканчивается символом новой строки.

Поэтому я сделал алго, который обрабатывается следующим образом:

 @Test public void empty() throws IOException { assertEquals(0, count("")); } @Test public void singleNewline() throws IOException { assertEquals(1, count("\n")); } @Test public void dataWithoutNewline() throws IOException { assertEquals(1, count("one")); } @Test public void oneCompleteLine() throws IOException { assertEquals(1, count("one\n")); } @Test public void twoCompleteLines() throws IOException { assertEquals(2, count("one\ntwo\n")); } @Test public void twoLinesWithoutNewlineAtEnd() throws IOException { assertEquals(2, count("one\ntwo")); } @Test public void aFewLines() throws IOException { assertEquals(5, count("one\ntwo\nthree\nfour\nfive\n")); } 

И это выглядит так:

 static long countLines(InputStream is) throws IOException { try(LineNumberReader lnr = new LineNumberReader(new InputStreamReader(is))) { char[] buf = new char[8192]; int n, previousN = -1; //Read will return at least one byte, no need to buffer more while((n = lnr.read(buf)) != -1) { previousN = n; } int ln = lnr.getLineNumber(); if (previousN == -1) { //No data read at all, ie file was empty return 0; } else { char lastChar = buf[previousN - 1]; if (lastChar == '\n' || lastChar == '\r') { //Ending with newline, deduct one return ln; } } //normal case, return line number + 1 return ln + 1; } } 

Если вы хотите получить интуитивные результаты, вы можете использовать это. Если вы просто хотите совместимость wc -l , просто используйте решение @ er.vikas, но не добавляйте его в результат и повторите пропуски:

 try(LineNumberReader lnr = new LineNumberReader(new FileReader(new File("File1")))) { while(lnr.skip(Long.MAX_VALUE) > 0){}; return lnr.getLineNumber(); } 

Как насчет использования classа Process из Java-кода? И затем чтение вывода команды.

 Process p = Runtime.getRuntime().exec("wc -l " + yourfilename); p.waitFor(); BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream())); String line = ""; int lineCount = 0; while ((line = b.readLine()) != null) { System.out.println(line); lineCount = Integer.parseInt(line); } 

Нужно попробовать. Опубликуют результаты.

Прямой путь с помощью сканера

 static void lineCounter (String path) throws IOException { int lineCount = 0, commentsCount = 0; Scanner input = new Scanner(new File(path)); while (input.hasNextLine()) { String data = input.nextLine(); if (data.startsWith("//")) commentsCount++; lineCount++; } System.out.println("Line Count: " + lineCount + "\t Comments Count: " + commentsCount); } 

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

Это смешное решение работает действительно хорошо!

 public static int countLines(File input) throws IOException { try (InputStream is = new FileInputStream(input)) { int count = 1; for (int aChar = 0; aChar != -1;aChar = is.read()) count += aChar == '\n' ? 1 : 0; return count; } } 

В системах на базе Unix используйте команду wc в командной строке.

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

Лучший оптимизированный код для многострочных файлов, не имеющих символа новой строки (‘\ n’) в EOF.

 /** * * @param filename * @return * @throws IOException */ public static int countLines(String filename) throws IOException { int count = 0; boolean empty = true; FileInputStream fis = null; InputStream is = null; try { fis = new FileInputStream(filename); is = new BufferedInputStream(fis); byte[] c = new byte[1024]; int readChars = 0; boolean isLine = false; while ((readChars = is.read(c)) != -1) { empty = false; for (int i = 0; i < readChars; ++i) { if ( c[i] == '\n' ) { isLine = false; ++count; }else if(!isLine && c[i] != '\n' && c[i] != '\r'){ //Case to handle line count where no New Line character present at EOF isLine = true; } } } if(isLine){ ++count; } }catch(IOException e){ e.printStackTrace(); }finally { if(is != null){ is.close(); } if(fis != null){ fis.close(); } } LOG.info("count: "+count); return (count == 0 && !empty) ? 1 : count; } 

если вы используете это

 public int countLines(String filename) throws IOException { LineNumberReader reader = new LineNumberReader(new FileReader(filename)); int cnt = 0; String lineRead = ""; while ((lineRead = reader.readLine()) != null) {} cnt = reader.getLineNumber(); reader.close(); return cnt; } 

вы не можете бегать до большого числа строк, ему нравится 100K строк, потому что return from reader.getLineNumber – int. вам нужен длинный тип данных для обработки максимальных строк.

Interesting Posts
Давайте будем гением компьютера.