2009-08-14 17 views
49

Ho un grosso file. Comprende circa 3.000-20.000 linee. Come posso ottenere il numero totale di righe nel file usando Java?Come posso ottenere il conteggio della linea in un file in modo efficiente?

+1

A giudicare dai vostri commenti alle risposte, la parola che stai cercando è 'efficiente', non e 'efficace' . – AakashM

+0

Sì, hai ragione. – firstthumb

+0

@Firstthumb: per favore non eliminare i commenti * dopo * le persone hanno risposto a loro. Rende il thread confuso per le persone che arrivano in ritardo allo show. – Telemachus

risposta

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

Aggiornamento: Per rispondere alla performance questione sollevata qui, ho fatto una misura. Prima cosa: 20.000 linee sono troppo poche, per far funzionare il programma per un tempo notevole. Ho creato un file di testo con 5 milioni di righe. Questa soluzione (iniziata con java senza parametri come -server o -XX-options) aveva bisogno di circa 11 secondi sulla mia scatola. Lo stesso con wc -l (UNIX command-line-tool per contare le linee), 11 secondi. La soluzione leggendo ogni singolo personaggio e cercando "\ n" aveva bisogno di 104 secondi, 9-10 volte tanto.

+0

Che effetto intendi? Prestazione? In questo caso non avrai modo migliore, perché le linee possono avere lunghezze diverse dovrai leggere il file completo, per contare i numeri di riga (wc lo fa anche tu). Se parli di efficienza di programmazione, sono sicuro che puoi metterlo in un metodo di utilità (o qualche libreria comune lo ha già fatto). – Mnementh

+0

@Firstthumb. Forse non efficiente, ma a chi importa. Sta contando solo 20k linee che sono piuttosto piccole. Questo codice ottiene il mio voto per essere il più semplice. –

+0

come funziona l'efficienza di LineNumberReader dal momento che estende BufferedReader? – Narayan

4

Leggere il file e contare il numero di caratteri di nuova riga. Un modo semplice per leggere un file in Java, una riga alla volta, è la classe java.util.Scanner.

0

Leggere il file riga per riga e incrementare un contatore per ogni riga finché non si ha letto l'intero file.

29

uso LineNumberReader

qualcosa come

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

Probabilmente dovresti anche chiudere() il lettore. –

+0

sì, fatto grazie: D – Narayan

+2

si dovrebbe probabilmente controllare lettore! = Null nel blocco finally – dfa

-2

Il lettore tamponata è eccessivo

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

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

La mia ricerca di un semplice esempio è createde uno questo è in realtà piuttosto scarsa. chiamare read() ripetutamente per un singolo carattere non è ottimale. vedi here per esempi e misurazioni.

+2

BufferedReader gestisce bene diverse terminazioni di linea. La tua soluzione ignora le terminazioni della linea Mac ('\ r'). Potrebbe essere OK. Ad ogni modo, la tua soluzione non viene effettivamente letta dal file al momento. Penso che tu abbia dimenticato una linea. – Mnementh

+5

Che cosa cambierà nextchar qui? Se hai intenzione di chiamare read() ad ogni iterazione, sospetto fortemente che un approccio BufferedReader sarà * molto * più veloce ... –

+0

è stata l'idea; -/Ho voluto scrivere l'esempio più semplice possibile. Mi chiedo quale sarebbe la differenza di velocità? – NSherwin

2

Tutte le risposte precedenti suggeriscono di leggere l'intero file e contare la quantità di nuove righe che si trovano durante l'operazione. Hai commentato alcuni come "non efficaci" ma questo è l'unico modo per farlo. Una "linea" non è nient'altro che un semplice carattere all'interno del file. E per contare quel personaggio devi dare un'occhiata a ogni singolo carattere all'interno del file.

Mi dispiace, ma non hai scelta. :-)

2

Se le risposte già pubblicate non sono abbastanza veloci, probabilmente dovrai cercare una soluzione specifica per il tuo particolare problema.

Ad esempio, se questi file di testo sono registri a cui si aggiungono solo i registri e regolarmente è necessario conoscere il numero di righe in essi contenute, è possibile creare un indice. Questo indice contiene il numero di righe nel file, quando il file è stato modificato l'ultima volta e quanto era grande il file. Questo ti permetterebbe di ricalcolare il numero di linee nel file saltando tutte le linee che avevi già visto e leggendo le nuove linee.

+0

+1 questo potrebbe essere un algoritmo online adatto. – zeroin23

-1

Probabilmente la soluzione più veloce in puro Java sarebbe quella di leggere il file come byte utilizzando un canale NIO in ByteBuffer di grandi dimensioni. Quindi, utilizzando le conoscenze dello schema di codifica dei file, si contano i byte codificati CR e/o NL, secondo la relativa convenzione del separatore di riga.

Le chiavi per il throughput massimizzare saranno:

  • assicurarsi di leggere il file in grossi pezzi,
  • evitare di copiare i byte da un buffer all'altro,
  • evitare di copiare/byte conversione in caratteri e
  • evitare di allocare oggetti per rappresentare le righe del file.

Il codice effettivo è troppo complicato per me da scrivere al volo. Inoltre, l'OP non sta chiedendo la soluzione più veloce.

1

Provare il comando "wc" di unix. Non intendo usarlo, voglio dire scaricare la fonte e vedere come lo fanno. Probabilmente è in c, ma puoi facilmente portare il comportamento a java. Il problema di crearne uno è quello di spiegare il problema finale di cr/lf.

3

Questo è quanto di efficiente quanto si può ottenere, tamponata lettura binaria, nessuna conversione di stringa,

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

rapido e sporco, ma non il lavoro:

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

} 
+0

Un effetto collaterale, questa soluzione non è multipiattaforma. – Stephan

9

ho trovato qualche soluzione per questo, potrebbe essere utile per voi

Di seguito è riportato il frammento di codice per, conteggiare il numero di righe dal file.

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(); 
+1

il risultato è line' count - 1' – MariuszS

+1

il risultato è 'lines + 1' –

+0

il risultato è getLineNumber() più 1 perché l'indice di riga inizia da 0 –

3

Hai bisogno del numero esatto di linee o solo la sua approssimazione? Mi capita di elaborare file di grandi dimensioni in parallelo e spesso non ho bisogno di sapere il conteggio esatto delle linee - poi tornerò al campionamento. Dividi il file in dieci blocchi da 1MB e conta le linee in ogni blocco, quindi moltiplicalo per 10 e otterrai un'approssimazione abbastanza buona del conteggio delle righe.

2

Questa soluzione è circa 3,6 × più veloce della risposta più votata quando viene testata su un file con 13,8 milioni di righe. Legge semplicemente i byte in un buffer e conta i caratteri \n. Si potrebbe giocare con la dimensione del buffer, ma sulla mia macchina, qualsiasi valore superiore a 8 KB non ha reso il codice più veloce.

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

Mi chiedo se l'utilizzo di un modello RegEx precompilato lo renderebbe più veloce o più lento.Quello che farebbe funzionare con tutte le terminazioni di linea, credo. E penso che potrebbe renderlo anche più veloce. – ingyhere

+0

Alcune delle soluzioni di cui sopra possono trarre vantaggio dal buffering, inoltre, se i vantaggi sarebbero di aiuto. Ad esempio, "new LineNumberReader (new FileReader (theFilePathStr), 8096)" o qualcosa del genere. – ingyhere

+0

Prestare attenzione alle codifiche dei caratteri ... Soluzione letto –

1

Vecchio post, ma ho una soluzione che potrebbe essere utile per la prossima gente. Perché non usare solo la lunghezza del file per sapere qual è la progressione? Naturalmente, le linee deve essere quasi la stessa dimensione, ma funziona molto bene per file di grandi dimensioni:

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

Permette di vedere la progressione senza fare alcun Leggi l'articolo completo sul file. So che dipende da molti elementi, ma spero che sarà utile :).

[Edizione] Ecco una versione con tempo stimato. Ho messo un po 'di SYSO per mostrare i progressi e la stima. Vedo che hai errori di stima del buon tempo dopo aver trattato una riga sufficiente (provo con le linee 10M e dopo l'1% del trattamento, la stima del tempo era esatta al 95%). Lo so, alcuni valori devono essere impostati in variabile. Questo codice è scritto rapidamente ma è utile per me. Spero che lo sarà anche per te :).

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))) + "%"); 

Sentiti libero di migliorare questo codice se pensi che sia una buona soluzione.

0

Nei miei test, le altre risposte richiedono ~ 150-300 ms su un file di linea 118.5k. Quanto segue richiede 1 ms ma è solo approssimativo (riporta linee 117k) e dipende dal fatto che ogni linea abbia una dimensione simile.

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

Java 8+ ha un modo molto bello e breve con NIO:

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

. possiamo avere un problema con il set di caratteri – Mikhail

+1

il set di caratteri è UTF-8 per impostazione predefinita –

Problemi correlati