Bestimmen Sie die Anzahl der Zeilen in einer Textdatei


209

Gibt es eine einfache Möglichkeit, die Anzahl der Zeilen in einer Textdatei programmgesteuert zu bestimmen?

Antworten:


396

Ernsthaft verspätete Bearbeitung: Wenn Sie .NET 4.0 oder höher verwenden

Die FileKlasse hat eine neue ReadLinesMethode, die Zeilen träge auflistet, anstatt sie alle gierig in ein Array wie zu lesen ReadAllLines. Jetzt können Sie sowohl Effizienz als auch Prägnanz erreichen mit:

var lineCount = File.ReadLines(@"C:\file.txt").Count();

Ursprüngliche Antwort

Wenn Sie sich nicht zu sehr um Effizienz kümmern, können Sie einfach schreiben:

var lineCount = File.ReadAllLines(@"C:\file.txt").Length;

Für eine effizientere Methode könnten Sie Folgendes tun:

var lineCount = 0;
using (var reader = File.OpenText(@"C:\file.txt"))
{
    while (reader.ReadLine() != null)
    {
        lineCount++;
    }
}

Bearbeiten: Als Antwort auf Fragen zur Effizienz

Der Grund, warum ich sagte, dass der zweite effizienter war, war die Speichernutzung, nicht unbedingt die Geschwindigkeit. Der erste lädt den gesamten Inhalt der Datei in ein Array, was bedeutet, dass mindestens so viel Speicher wie die Größe der Datei zugewiesen werden muss. Die zweite Schleife führt jeweils nur eine Zeile durch, sodass nie mehr als eine Zeile Speicherplatz gleichzeitig zugewiesen werden muss. Dies ist für kleine Dateien nicht so wichtig, aber für größere Dateien kann es ein Problem sein (wenn Sie versuchen, die Anzahl der Zeilen in einer 4-GB-Datei auf einem 32-Bit-System zu ermitteln, wo beispielsweise einfach nicht genug vorhanden ist Adressraum im Benutzermodus, um ein so großes Array zuzuweisen).

In Bezug auf die Geschwindigkeit würde ich nicht erwarten, dass viel drin ist. Es ist möglich, dass ReadAllLines einige interne Optimierungen aufweist, andererseits muss möglicherweise ein großer Teil des Speichers zugewiesen werden. Ich würde vermuten, dass ReadAllLines für kleine Dateien schneller, für große Dateien jedoch erheblich langsamer ist. Die einzige Möglichkeit, dies festzustellen, besteht darin, es mit einer Stoppuhr oder einem Code-Profiler zu messen.


2
Kleiner Hinweis: Da String ein Referenztyp ist, hat das Array die Größe der Anzahl der Zeilen x die Größe eines Zeigers. Sie haben jedoch Recht, dass der Text weiterhin gespeichert werden muss, wobei jede Zeile als einzelnes String-Objekt gespeichert wird.
Mike Dimmick

15
Zu Ihrer Information: Um dies zu tun, müssen ReadLines().Count()Sie using System.LinqIhren Includes ein hinzufügen . Es schien ziemlich unintuitiv zu sein, diesen Zusatz zu verlangen, deshalb erwähne ich ihn. Wenn Sie Visual Studio verwenden, wird dieser Zusatz wahrscheinlich automatisch für Sie ausgeführt.
Nucleon

2
Ich habe beide Ansätze getestet: "File.ReadLines.Count ()" v / s "reader.ReadLine ()" und "reader.ReadLine ()" sind etwas schneller, aber mit sehr geringem Abstand schneller. "ReadAllLines" ist lockerer, was doppelt so viel Zeit in Anspruch nimmt und viel Speicherplatz beansprucht. Dies liegt daran, dass "File.ReadLines.Count ()" und "reader.ReadLine ()" ein Enumerator sind, der Dateien zeilenweise liest und nicht die gesamte Datei in den Speicher lädt, sondern sie erneut im RAM liest.
Yogee

9
Ja, niemand arbeitet jemals mit Dateien ab 4 GB. Wir haben es sicherlich nie mit so großen Protokolldateien zu tun. Oh, Moment mal.
Greg Beech

2
Wenn Sie die Innenseiten von File.ReadLines () sehen möchten, gehen Sie hier: System.IO.File.cs Wenn Sie einen Drilldown durch die Überladungen durchführen, gelangen Sie hierher: ReadLinesIterator.cs
Steve Kinyon

12

Das einfachste:

int lines = File.ReadAllLines("myfile").Length;

8

Dies würde weniger Speicher verbrauchen, aber wahrscheinlich länger dauern

int count = 0;
string line;
TextReader reader = new StreamReader("file.txt");
while ((line = reader.ReadLine()) != null)
{
  count++;
}
reader.Close();

5

Wenn mit einfach eine Codezeile gemeint ist, die leicht zu entschlüsseln, aber zufällig ineffizient ist?

string[] lines = System.IO.File.RealAllLines($filename);
int cnt = lines.Count();

Das ist wahrscheinlich der schnellste Weg, um zu wissen, wie viele Zeilen.

Sie können dies auch tun (je nachdem, ob Sie es einpuffern)

#for large files
while (...reads into buffer){
string[] lines = Regex.Split(buffer,System.Enviorment.NewLine);
}

Es gibt viele andere Möglichkeiten, aber eine der oben genannten ist wahrscheinlich die, mit der Sie gehen werden.


3
Ich behaupte, dass diese Methode sehr ineffizient ist; weil Sie die gesamte Datei in den Speicher und nicht weniger in ein String-Array einlesen. Sie müssen den Puffer nicht kopieren, wenn Sie ReadLine verwenden. Siehe die Antwort von @GregBeech. Tut mir leid, auf deiner Parade zu regnen.
Mike Christian

2

Sie können es schnell einlesen und einen Zähler erhöhen. Verwenden Sie einfach eine Schleife, um ihn zu erhöhen, und tun Sie nichts mit dem Text.


3
Dies sollte ein Kommentar sein, keine Antwort.
IamBatman

2

Das Einlesen einer Datei an und für sich dauert einige Zeit. Das Sammeln des Ergebnisses durch Müll ist ein weiteres Problem, da Sie die gesamte Datei lesen, um nur die Zeilenumbruchzeichen zu zählen.

Irgendwann muss jemand die Zeichen in der Datei lesen, unabhängig davon, ob dies das Framework ist oder ob es Ihr Code ist. Dies bedeutet, dass Sie die Datei öffnen und in den Speicher einlesen müssen, wenn die Datei groß ist. Dies ist möglicherweise ein Problem, da der Speicher durch Müll gesammelt werden muss.

Nima Ara hat eine nette Analyse gemacht, die Sie in Betracht ziehen könnten

Hier ist die vorgeschlagene Lösung, da sie 4 Zeichen gleichzeitig liest, das Zeilenvorschubzeichen zählt und dieselbe Speicheradresse für den nächsten Zeichenvergleich erneut verwendet.

private const char CR = '\r';  
private const char LF = '\n';  
private const char NULL = (char)0;

public static long CountLinesMaybe(Stream stream)  
{
    Ensure.NotNull(stream, nameof(stream));

    var lineCount = 0L;

    var byteBuffer = new byte[1024 * 1024];
    const int BytesAtTheTime = 4;
    var detectedEOL = NULL;
    var currentChar = NULL;

    int bytesRead;
    while ((bytesRead = stream.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
    {
        var i = 0;
        for (; i <= bytesRead - BytesAtTheTime; i += BytesAtTheTime)
        {
            currentChar = (char)byteBuffer[i];

            if (detectedEOL != NULL)
            {
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 1];
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 2];
                if (currentChar == detectedEOL) { lineCount++; }

                currentChar = (char)byteBuffer[i + 3];
                if (currentChar == detectedEOL) { lineCount++; }
            }
            else
            {
                if (currentChar == LF || currentChar == CR)
                {
                    detectedEOL = currentChar;
                    lineCount++;
                }
                i -= BytesAtTheTime - 1;
            }
        }

        for (; i < bytesRead; i++)
        {
            currentChar = (char)byteBuffer[i];

            if (detectedEOL != NULL)
            {
                if (currentChar == detectedEOL) { lineCount++; }
            }
            else
            {
                if (currentChar == LF || currentChar == CR)
                {
                    detectedEOL = currentChar;
                    lineCount++;
                }
            }
        }
    }

    if (currentChar != LF && currentChar != CR && currentChar != NULL)
    {
        lineCount++;
    }
    return lineCount;
}

Oben sehen Sie, dass eine Zeile auch vom zugrunde liegenden Framework zeichenweise gelesen wird, da Sie alle Zeichen lesen müssen, um den Zeilenvorschub zu sehen.

Wenn Sie es als erledigt in Bay Nima profilieren, werden Sie feststellen, dass dies eine ziemlich schnelle und effiziente Methode ist.


1

Zählen Sie die Wagenrückläufe / Zeilenvorschübe. Ich glaube an Unicode sind sie immer noch 0x000D bzw. 0x000A. Auf diese Weise können Sie so effizient oder ineffizient sein, wie Sie möchten, und entscheiden, ob Sie mit beiden Charakteren umgehen müssen oder nicht


1

Eine praktikable Option, die ich persönlich verwendet habe, wäre das Hinzufügen eines eigenen Headers zur ersten Zeile der Datei. Ich habe dies für ein benutzerdefiniertes Modellformat für mein Spiel getan. Grundsätzlich habe ich ein Tool, das meine OBJ-Dateien optimiert, den nicht benötigten Mist beseitigt, sie in ein besseres Layout konvertiert und dann die Gesamtzahl der Linien, Flächen, Normalen, Scheitelpunkte und Textur-UVs aufschreibt die allererste Zeile. Diese Daten werden dann von verschiedenen Array-Puffern verwendet, wenn das Modell geladen wird.

Dies ist auch nützlich, da Sie die Datei nur einmal durchlaufen müssen, um sie zu laden, anstatt einmal, um die Zeilen zu zählen und die Daten erneut in Ihre erstellten Puffer einzulesen.


-1
try {
    string path = args[0];
    FileStream fh = new FileStream(path, FileMode.Open, FileAccess.Read);
    int i;
    string s = "";
    while ((i = fh.ReadByte()) != -1)
        s = s + (char)i;

    //its for reading number of paragraphs
    int count = 0;
    for (int j = 0; j < s.Length - 1; j++) {
            if (s.Substring(j, 1) == "\n")
                count++;
    }

    Console.WriteLine("The total searches were :" + count);

    fh.Close();

} catch(Exception ex) {
    Console.WriteLine(ex.Message);
}         

4
-1: Dies wird LANGSAM sein, viel Speicher verbrauchen und GC harte Zeit geben!
ya23

-2

Sie können die ausführbare Datei " wc .exe" (im Lieferumfang von UnixUtils enthalten und muss nicht installiert werden) als externen Prozess ausführen. Es unterstützt verschiedene Zeilenzählmethoden (wie Unix vs Mac vs Windows).


Auf keinen Fall wäre dies schnell genug, um nützlich zu sein. Der Aufwand für das Aufrufen der ausführbaren Datei wäre doppelt so hoch (offensichtliche Übertreibung ist offensichtlich) wie für eine einzelne Inkrementierungsschleife.
Krythic
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.