Frage

Ich verwende große Datendateien, manchmal nur ich die Anzahl der Zeilen in diesen Dateien wissen müssen, in der Regel Ich öffne sie und sie Zeile für Zeile gelesen, bis ich das Ende der Datei

erreichen

Ich habe mich gefragt, ob es eine intelligentere Art und Weise, dass

zu tun ist,
War es hilfreich?

Lösung

Dies ist die schnellste Version, die ich bisher gefunden habe, etwa 6 mal schneller als Leseleitungen. Auf einer 150 MB-Protokolldatei das dauert 0,35 Sekunden, im Vergleich zu 2,40 Sekunden, wenn readlines () verwenden. Just for fun, linux‘wc -l Befehl dauert 0,15 Sekunden.

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

EDIT, 9 1/2 Jahre später: Ich habe praktisch keine Java-Erfahrung, aber irgendwie habe ich Benchmark diesen Code gegen die LineNumberReader Lösung versucht, unten, da es störte mich, dass niemand es tat. Es scheint, dass vor allem für große Dateien meiner Lösung ist schneller. Obwohl es scheint, ein paar Durchläufe dauern, bis der Optimierer einen anständigen Job tut. Ich habe ein bisschen mit dem Code gespielt, und habe eine neue Version erzeugt, die konsequent am schnellsten ist:

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<readChars; ++i) {
                if (c[i] == '\n') {
                    ++count;
                }
            }
            readChars = is.read(c);
        }

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

Benchmark resuls für eine 1.3GB Textdatei, y-Achse in Sekunden. Ich habe 100 läuft mit der gleichen Datei ausgeführt wird, und jeden Lauf mit System.nanoTime() gemessen. Sie können sehen, dass countLinesOld ein paar Ausreißer hat, und countLinesNew keine hat und während es nur ein bisschen schneller ist, ist der Unterschied statistisch signifikant. LineNumberReader ist deutlich langsamer.

 Benchmark Plot

Andere Tipps

ich eine andere Lösung für das Problem eingeführt habe, fand ich es effiziente Zeilen zu zählen:

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
}

Die akzeptierte Antwort hat durch einen Fehler für Multi-Line-Dateien einen aus, die in Newline am Ende nicht. Eine eine Zeile Datei, ohne eine neue Zeile endet würde 1 zurück, aber eine zweizeilige Datei ohne eine neue Zeile enden würde 1 auch zurück. Hier ist eine Implementierung der akzeptierten Lösung, die dies festlegt. Die endsWithoutNewLine Kontrollen sind verschwenderisch für alles, aber die letzte Lese, sollen aber trivial Zeit klug sein, verglichen mit der Gesamtfunktion.

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

Mit getaggt Sie können Streams verwenden:

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

Die Antwort mit der Methode count () gab mir über die Linie verzählt, wenn eine Datei nicht eine neue Zeile am Ende der Datei hat -. Es versäumt, die letzte Zeile in der Datei zu zählen

Diese Methode funktioniert besser für mich:

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

Ich weiß, dass dies eine alte Frage, aber die akzeptierte Lösung nicht ganz überein, was ich brauchte, es zu tun. Also, ich habe es verfeinerte verschiedene Leitungsabschlüsse zu akzeptieren (und nicht nur Zeilenvorschub) und eine bestimmte Zeichencodierung zu verwenden (anstelle von ISO-8859 n ). Alle in einem Verfahren (refactor je nach Bedarf):

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

Diese Lösung ist vergleichbar in der Geschwindigkeit auf die akzeptierte Lösung, etwa 4% langsamer in meinen Tests (obwohl Timing-Tests in Java notorisch unzuverlässig sind).

I getestet, um die oben genannten Methoden für die Linien zu zählen und hier sind meine Beobachtungen für verschiedene Methoden auf meinem System wie getestet

Dateigröße: 1,6 Gb Methoden:

  1. Mit Scanner : 35s ca.
  2. Mit BufferedReader : 5s ca.
  3. Mit Java 8 : 5s ca.
  4. Mit LineNumberReader : 5s ca.

Darüber hinaus Java8 Ansatz scheint recht praktisch: Files.lines (Paths.get (filePath), Charset.defaultCharset ()) zählen () [Rückgabetyp: long].

/**
 * Count file rows.
 *
 * @param file file
 * @return file row count
 * @throws IOException
 */
public static long getLineCount(File file) throws IOException {

    try (Stream<String> lines = Files.lines(file.toPath())) {
        return lines.count();
    }
}

Getestet auf JDK8_u31. Aber in der Tat Leistung ist langsam im Vergleich zu dieser Methode:

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

Geprüft und sehr schnell.

Eine straight-forward Weise mit 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);
    }

ich festgestellt, dass wc -l: s Methode von Zeilenumbrüchen ist in Ordnung zu zählen, gibt aber nicht-intuitive Ergebnisse auf Dateien, in denen die letzte Zeile nicht mit einer Neuen-Zeile beenden.

Und @ er.vikas Lösung auf LineNumberReader basiert aber das Hinzufügen einer zur Linie nicht-intuitive Ergebnisse auf Dateien Zählung zurückgeführt, wo die letzte Zeile mit Newline endet.

Ich habe daher eine algo die Griffe wie folgt:

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

Und es sieht wie folgt aus:

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, i.e 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;
    }
}

Wenn Sie intuitive Ergebnisse möchten, können Sie diese verwenden. Wenn Sie nur wc -l Kompatibilität, einfache Bedienung @ er.vikas Lösung, aber fügen Sie nicht ein zu dem Ergebnis, und wiederholen Sie die Überspringen:

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

Wie wäre es die Process-Klasse aus Java-Code verwenden? Und dann die Ausgabe des Befehls zu lesen.

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

Sie benötigen es aber zu versuchen. Werden die Ergebnisse veröffentlichen.

Wenn Sie keine Indexstrukturen haben, werden Sie nicht um das Lesen der kompletten Datei. Aber man kann es optimieren, indem vermieden wird Zeile für Zeile zu lesen und einen regulären Ausdruck verwenden, um alle Leitungsabschlüsse zu entsprechen.

Diese lustige Lösung funktioniert wirklich gut tatsächlich!

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

Auf Unix-basierten Systemen, verwenden Sie den Befehl wc auf der Kommandozeile.

Die einzige Weg zu wissen, wie viele Zeilen es in der Datei ist, ist, sie zu zählen. Sie können natürlich auch eine Metrik von Daten erstellen Sie eine durchschnittliche Länge einer Zeile geben und dann die Dateigröße erhalten und teilen, dass mit durchschnittl. Länge aber nicht genau sein wird.

Best optimierten Code für Multi-Line-Dateien keine Newline mit ( '\ n') Zeichen bei 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;
}

Scanner mit regex:

public int getLineCount() {
    Scanner fileScanner = null;
    int lineCount = 0;
    Pattern lineEndPattern = Pattern.compile("(?m)$");  
    try {
        fileScanner = new Scanner(new File(filename)).useDelimiter(lineEndPattern);
        while (fileScanner.hasNext()) {
            fileScanner.next();
            ++lineCount;
        }   
    }catch(FileNotFoundException e) {
        e.printStackTrace();
        return lineCount;
    }
    fileScanner.close();
    return lineCount;
}

Haben es nicht getaktet wird.

Wenn Sie verwenden diese

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

Sie große num Reihen laufen kippen, mag 100K Reihen, weil Rückkehr aus reader.getLineNumber int ist. Sie müssen lange Art von Daten maximal Zeilen zu verarbeiten ..

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top