Anzahl der Zeilen in einer Datei in Java

Lesezeit: 9 Minuten

Anzahl der Zeilen in einer Datei in Java
Kennzeichen

Ich verwende riesige Datendateien, manchmal muss ich nur die Anzahl der Zeilen in diesen Dateien kennen, normalerweise öffne ich sie und lese sie Zeile für Zeile, bis ich das Ende der Datei erreiche

Ich habe mich gefragt, ob es einen intelligenteren Weg gibt, dies zu tun

1646310429 363 Anzahl der Zeilen in einer Datei in Java
Martinus

Dies ist die schnellste Version, die ich bisher gefunden habe, etwa 6-mal schneller als readLines. Bei einer 150-MB-Protokolldatei dauert dies 0,35 Sekunden, gegenüber 2,40 Sekunden bei Verwendung von readLines(). Nur zum Spaß, der Befehl wc -l von Linux 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 trotzdem habe ich versucht, diesen Code mit dem zu vergleichen LineNumberReader Lösung unten, da es mich störte, dass es niemand getan hat. Es scheint, dass meine Lösung besonders für große Dateien schneller ist. Obwohl es einige Durchläufe zu dauern scheint, bis der Optimierer einen anständigen Job macht. Ich habe ein bisschen mit dem Code gespielt und eine neue Version erstellt, die konstant 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-Ergebnisse für eine 1,3-GB-Textdatei, Y-Achse in Sekunden. Ich habe 100 Läufe mit der gleichen Datei durchgeführt und jeden Lauf mit gemessen System.nanoTime(). Sie können sehen, dass countLinesOld hat ein paar Ausreißer, und countLinesNew hat keine und obwohl es nur ein bisschen schneller ist, ist der Unterschied statistisch signifikant. LineNumberReader ist deutlich langsamer.

Benchmark-Plot

  • BufferedInputStream sollte das Puffern für Sie übernehmen, daher sehe ich nicht, wie ein Zwischenbyte verwendet wird[] array wird es nicht schneller machen. Es ist unwahrscheinlich, dass Sie viel besser abschneiden, als readLine() wiederholt zu verwenden (da dies von der API optimiert wird).

    – Wds

    17. Januar 2009 um 13:23 Uhr

  • Sie werden diesen InputStream schließen, wenn Sie damit fertig sind, nicht wahr?

    – beugen

    24. Mai 2009 um 18:15 Uhr

  • Wenn das Puffern geholfen hätte, würde es das tun, weil BufferedInputStream standardmäßig 8 KB puffert. Erhöhen Sie Ihr Byte[] auf diese Größe oder größer und Sie können den BufferedInputStream löschen. versuchen Sie zB 1024*1024 Bytes.

    – Peter Lawrey

    24. Mai 2009 um 19:02 Uhr

  • Zwei Dinge: (1) Die Definition eines Zeilenabschlusszeichens im Java-Quelltext ist ein Wagenrücklauf, ein Zeilenvorschub oder ein Wagenrücklauf gefolgt von einem Zeilenvorschub. Ihre Lösung funktioniert nicht für CR, die als Leitungsabschluss verwendet werden. Zugegeben, das einzige Betriebssystem, von dem ich mir vorstellen kann, dass CR als Standard-Zeilenabschlusszeichen verwendet, ist Mac OS vor Mac OS X. (2) Ihre Lösung geht von einer Zeichencodierung wie US-ASCII oder UTF-8 aus. Die Zeilenanzahl kann für Kodierungen wie UTF-16 ungenau sein.

    – Nathan Ryan

    21. September 2012 um 11:58 Uhr

  • Toller Code … für eine 400-MB-Textdatei dauerte es nur eine Sekunde. Vielen Dank @Martinus

    – Benutzer3181500

    2. November 2017 um 12:43 Uhr

1646310430 463 Anzahl der Zeilen in einer Datei in Java
er.vikas

Ich habe eine andere Lösung für das Problem implementiert, ich fand es effizienter beim Zählen von Zeilen:

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
}

  • LineNumberReader‘S lineNumber Feld ist eine Ganzzahl … Wird es nicht einfach für Dateien umbrochen, die länger als Integer.MAX_VALUE sind? Warum sollten Sie hier lange vorbeispringen?

    – epb

    3. April 2015 um 20:27 Uhr

  • Das Hinzufügen von eins zur Zählung ist eigentlich falsch. wc -l zählt die Anzahl der Newline-Zeichen in der Datei. Dies funktioniert, da jede Zeile mit einem Zeilenumbruch abgeschlossen wird, einschließlich der letzten Zeile in einer Datei. Jede Zeile hat ein Zeilenumbruchzeichen, einschließlich der leeren Zeilen, daher ist die Anzahl der Zeilenumbruchzeichen == Anzahl der Zeilen in einer Datei. Jetzt die lineNumber variabel ein FileNumberReader stellt auch die Anzahl der gesehenen Zeilenumbruchzeichen dar. Sie beginnt bei Null, bevor ein Zeilenumbruch gefunden wurde, und wird mit jedem gesehenen Zeilenumbruchzeichen erhöht. Fügen Sie also bitte keine Eins zur Zeilennummer hinzu.

    – Alexander Torstling

    16. Februar 2016 um 14:06 Uhr

  • @PB_MLT: Obwohl Sie Recht haben, dass eine Datei mit einer einzelnen Zeile ohne Zeilenumbruch als 0 Zeilen gemeldet würde, ist dies der Fall wc -l meldet auch diese Art von Datei. Siehe auch stackoverflow.com/questions/729692/…

    – Alexander Torstling

    16. Februar 2016 um 14:10 Uhr

  • @PB_MLT: Das gegenteilige Problem tritt auf, wenn die Datei nur aus einem Zeilenumbruch besteht. Ihr vorgeschlagener Algorithmus würde 0 und zurückgeben wc -l würde 1 zurückgeben. Ich kam zu dem Schluss, dass alle Methoden Fehler aufweisen, und implementierte eine basierend auf dem Verhalten, das ich mir wünsche, siehe meine andere Antwort hier.

    – Alexander Torstling

    16. Februar 2016 um 14:50 Uhr

  • Ich habe diese Antwort abgelehnt, weil es scheint, dass keiner von Ihnen sie bewertet hat

    – Amstegraf

    1. Februar 2017 um 19:01 Uhr

Die akzeptierte Antwort enthält einen Off-by-One-Fehler für mehrzeilige Dateien, die nicht mit einem Zeilenumbruch enden. Eine einzeilige Datei, die ohne Zeilenumbruch endet, würde 1 zurückgeben, aber eine zweizeilige Datei, die ohne Zeilenumbruch endet, würde auch 1 zurückgeben. Hier ist eine Implementierung der akzeptierten Lösung, die dies behebt. Die EndsWithoutNewLine-Prüfungen sind verschwenderisch für alles außer dem endgültigen Lesen, sollten aber im Vergleich zur Gesamtfunktion trivial sein.

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

  • Guter Fang. Ich bin mir nicht sicher, warum Sie die akzeptierte Antwort nicht einfach bearbeitet und eine Notiz in einem Kommentar gemacht haben. Die meisten Leute werden nicht so weit lesen.

    – Ryan

    11. Dezember 2013 um 21:33 Uhr

  • @Ryan, es fühlte sich einfach nicht richtig an, eine 4 Jahre alte akzeptierte Antwort mit über 90 positiven Stimmen zu bearbeiten.

    – DMulligan

    12. Dezember 2013 um 6:47 Uhr

  • @AFinkelstein, ich denke, das macht diese Seite so großartig, dass du kann Bearbeiten Sie die Antwort mit der höchsten Bewertung.

    – Sebastian

    27. Januar 2014 um 8:48 Uhr

  • Diese Lösung verarbeitet keinen Wagenrücklauf (\r) und Wagenrücklauf gefolgt von einem Zeilenvorschub (\r\n)

    – Simon Brandhof – SonarSource

    5. Februar 2014 um 13:36 Uhr

  • @Simon Brandhof, ich bin verwirrt darüber, warum ein Wagenrücklauf als eine andere Zeile gezählt wird? Ein “\n” ist ein Wagenrücklauf-Zeilenvorschub, also versteht jeder, der “\r\n” schreibt, etwas nicht … Außerdem sucht er Zeichen für Zeichen, also bin ich mir ziemlich sicher, ob jemand “\r \n” würde es immer noch das “\n” abfangen und die Zeile zählen. Jedenfalls denke ich, dass er es ganz gut auf den Punkt gebracht hat. Es gibt jedoch viele Szenarien, in denen dies nicht ausreicht, um eine Zeilenanzahl zu erhalten.

    – nckbrz

    8. April 2014 um 3:46 Uhr


1646310431 646 Anzahl der Zeilen in einer Datei in Java
msayag

Mit Java-8 können Sie Streams verwenden:

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

Die Antwort mit der obigen Methode count() gab mir Zeilenfehler, wenn eine Datei am Ende der Datei keinen Zeilenumbruch hatte – die letzte Zeile in der Datei konnte nicht gezählt werden.

Diese Methode funktioniert bei mir besser:

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

  • In diesem Fall muss LineNumberReader nicht verwendet werden. Verwenden Sie einfach BufferedReader. In diesem Fall haben Sie die Flexibilität, lange Datentypen zu verwenden cnt.

    – Syed Aqeel Ashiq

    30. Januar 2014 um 8:02 Uhr

  • [INFO] PMD-Fehler:xx:19 Regel:EmptyWhileStmt Priorität:3 Vermeiden Sie leere While-Anweisungen.

    – Tschhorn Elit

    1. Januar 2020 um 16:49 Uhr

1646310431 826 Anzahl der Zeilen in einer Datei in Java
whoami – fakeFaceTrueSoul

Ich habe die oben genannten Methoden zum Zählen von Linien getestet und hier sind meine Beobachtungen für verschiedene Methoden, die auf meinem System getestet wurden

Dateigröße: 1,6 GB Methoden:

  1. Scanner verwenden : 35s ca
  2. Verwenden von BufferedReader : 5s ca
  3. Verwendung von Java 8 : 5s ca
  4. Verwenden von LineNumberReader : 5s ca

Außerdem Java8 Ansatz scheint ganz praktisch :

Files.lines(Paths.get(filePath), Charset.defaultCharset()).count()
[Return type : long]

  • In diesem Fall muss LineNumberReader nicht verwendet werden. Verwenden Sie einfach BufferedReader. In diesem Fall haben Sie die Flexibilität, lange Datentypen zu verwenden cnt.

    – Syed Aqeel Ashiq

    30. Januar 2014 um 8:02 Uhr

  • [INFO] PMD-Fehler:xx:19 Regel:EmptyWhileStmt Priorität:3 Vermeiden Sie leere While-Anweisungen.

    – Tschhorn Elit

    1. Januar 2020 um 16:49 Uhr

1646310432 808 Anzahl der Zeilen in einer Datei in Java
Nathan Ryan

Ich weiß, dass dies eine alte Frage ist, aber die akzeptierte Lösung entsprach nicht ganz dem, was ich brauchte. Also habe ich es verfeinert, um verschiedene Zeilenabschlusszeichen zu akzeptieren (statt nur Zeilenvorschub) und eine bestimmte Zeichencodierung zu verwenden (statt ISO-8859-n). All-in-One-Methode (gegebenenfalls umgestalten):

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 in der Geschwindigkeit vergleichbar mit der akzeptierten Lösung, etwa 4 % langsamer in meinen Tests (obwohl Timing-Tests in Java notorisch unzuverlässig sind).

923360cookie-checkAnzahl der Zeilen in einer Datei in Java

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy