Número de linhas em um arquivo em Java

Eu uso arquivos de dados enormes, às vezes eu só preciso saber o número de linhas nesses arquivos, normalmente eu os abro e leio linha por linha até chegar ao final do arquivo

Eu queria saber se existe uma maneira mais inteligente de fazer isso

   

    Esta é a versão mais rápida que encontrei até agora, cerca de 6 vezes mais rápida que readLines. Em um arquivo de log de 150MB, isso leva 0,35 segundos, contra 2,40 segundos quando se usa o readLines (). Apenas por diversão, o comando linux ‘wc -l leva 0,15 segundos.

    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 anos e meio depois: Eu praticamente não tenho experiência em java, mas de qualquer maneira eu tentei comparar este código com a solução LineNumberReader abaixo, uma vez que me incomodou que ninguém fizesse isso. Parece que, especialmente para arquivos grandes, minha solução é mais rápida. Embora pareça demorar algumas execuções até que o otimizador faça um trabalho decente. Eu joguei um pouco com o código e produzi uma nova versão que é consistentemente mais rápida:

     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 

    Resultados de referência para um arquivo de texto de 1,3 GB, eixo y em segundos. Eu executei 100 execuções com o mesmo arquivo e medi cada execução com System.nanoTime() . Você pode ver que countLines tem alguns outliers e countLinesNew não tem nenhum e parece ser também um pouco mais rápido. LineNumberReader é claramente mais lento.

    Gráfico de referência

    Eu implementei outra solução para o problema, achei mais eficiente na contagem de linhas:

     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 } 

    A resposta aceita tem um erro de logoff para arquivos de várias linhas que não terminam em nova linha. Um arquivo de uma linha terminando sem uma nova linha retornaria 1, mas um arquivo de duas linhas terminando sem uma nova linha retornaria 1 também. Aqui está uma implementação da solução aceita que corrige isso. As verificações endsWithoutNewLine são um desperdício para tudo, menos para a leitura final, mas devem ser triviais em comparação com a function geral.

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

    Com o java-8 , você pode usar streams:

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

    A resposta com o método count () acima me dava erros de conta se um arquivo não tivesse uma nova linha no final do arquivo – ele não contava a última linha no arquivo.

    Este método funciona melhor para mim:

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

    Eu sei que esta é uma pergunta antiga, mas a solução aceita não combinava com o que eu precisava fazer. Então, eu o refinei para aceitar vários terminadores de linha (em vez de apenas alimentação de linha) e usar uma codificação de caracteres especificada (em vez de ISO-8859- n ). Tudo em um método (refatorar conforme apropriado):

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

    Esta solução é comparável em velocidade à solução aceita, cerca de 4% mais lenta em meus testes (embora os testes de temporização em Java sejam notoriamente não confiáveis).

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

    Testado no JDK8_u31. Mas, na verdade, o desempenho é lento comparado a esse método:

     /** * 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; } } 

    Testado e muito rápido.

    Concluí que o método wc -l : s de contagem de novas linhas é bom, mas retorna resultados não intuitivos em arquivos em que a última linha não termina com uma nova linha.

    E a solução @ er.vikas baseada em LineNumberReader, mas adicionando uma à contagem de linha, retornou resultados não intuitivos em arquivos em que a última linha termina com nova linha.

    Eu, portanto, fiz um algoritmo que lida da seguinte maneira:

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

    E parece assim:

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

    Se você quiser resultados intuitivos, você pode usar isso. Se você quer apenas compatibilidade wc -l , use a solução @ er.vikas, mas não adicione uma ao resultado e repita o salto:

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

    Que tal usar a class Process dentro do código Java? E então lendo a saída do comando.

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

    Precisa experimentar embora. Vai postar os resultados.

    Uma maneira direta usando o Scanner

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

    Se você não tem nenhuma estrutura de índice, você não terá a leitura do arquivo completo. Mas você pode otimizá-lo evitando lê-lo linha por linha e usar um regex para combinar com todos os terminadores de linha.

    Esta solução engraçada funciona muito bem, na verdade!

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

    Em sistemas baseados em Unix, use o comando wc na linha de comandos.

    A única maneira de saber quantas linhas existem no arquivo é contá-las. É claro que você pode criar uma métrica a partir de seus dados, dando a você uma duração média de uma linha e, em seguida, obter o tamanho do arquivo e dividi-lo com a média. comprimento, mas isso não será preciso.

    Melhor código otimizado para arquivos de várias linhas sem caractere de nova linha (‘\ n’) no 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; } 

    se você usar isso

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

    você não pode correr para big num linhas, gosta de 100 mil linhas, porque o retorno de reader.getLineNumber é int. você precisa de um tipo longo de dados para processar as linhas máximas.