So ne kleinere Spielerei nebenbei, aber auch hübsch, um zu sehen, was Perl so kann: Wie viele zeilen hat eine Datei?
Man will also wissen, wie viele Zeilen in einer Datei stehen, gesucht wird eine möglichst effektive und Speicherschonende Möglichkeit, dies herauszufinden.
Variante 1: Datei einlesen und zählen
Man liest also die Datei komplett ein und läßt die zeilen zählen, etwa so
open (my $in,'<','test.txt');
@zeilen= <$in>; close $in;
$zeilenanzahl=@zeilen; print "Die Datei hat $zeilen Zeilen";
Ja, das geht, aber: Wenn die Datei mehrere Megabyte oder sogar Gigabyte hat, wirds dem Rechner ein bißchen viel...
Die ganze Datei einlesen ist also schon mal blöd.
Ha, Idee, Zeilen nacheinander einlesen:
$zeilen=0; open (my $in,'<','test.txt'); while (<$in>){$zeilen++;} close $in;
$zeilenanzahl=@zeilen; print "Die Datei hat $zeilen Zeilen";
Bingo, geht auch und liest nur einzelne Zeilen ein. Aber Vorsicht: Wenn eine Zeile mehrere MB oder gar GB hat, wirds dem Rechner auch wieder ein bißchen viel, denn pro Zeile wird die aktuelle Zeile $_ zugeordnet, also eingelesen!
Danach wir per read jeweils ein Datenblock von einem Megabyte in $_ eingelesen und die darin enthaltenen \n's gezählt (tr /\n/\n/) und $cou hinzugefügt.
Wieso ist das nun besser?
Weil immer nur maximal ein MB Daten eingelesen werden. Auf Wunsch kann man den Puffer natürlich verkleinern oder vergrößern...
Man sollte es immer vermeiden, Dateien komplett einzulesen, wenn es geht. Sehr lange Dateien können, wenn sie in den Rechnerspeicher eingelesen werden, den Rechner so ziemlich lahm legen!
In diesem Sinne, bis zum nächsten Mal...
Kommentare zum Beitrag "Wie viele Zeilen hat die Datei?"
Kommentar von Renée Bäcker
Wenn es nur um das "Zählen" mit tr/// geht, kannst Du den Ersetzungsteil leer lassen, also
tr/\n//
Solange keine Modifier (tr/\n//cd o.ä.) angegeben werden, wird nichts ersetzt sondern nur gezählt. So geräts Du nicht in Gefahr, durch einen Tippfehler alles zu ersetzen (tr/\n/n/).
Kommentar von Struppi
Warum nicht einfach:
open FH, "test.txt"; while(<FH>){} print "Zeilen $."; close FH;
Kommentar von Admin
Nunja, wie ich oben bereits geschrieben habe, wird auch bei
while (<in>){}
die Datei komplett in den Speicher eingelesen. Zwar zeilenweise, aber was, wenn eine Zeile mal 'nen GB hat?
Und um das nachzuvollziehen, kann man ja mal 'ne 1 GB-Datei anlegen, wobei das eine GB in nur einer Zeile steht, also ohne breaks.
Diese eine Zeile liest man dann mal ein... und geht dann Kaffee kochen. Also bei mir hier dauerte es mehrere Minuten, bis sich Perl wieder zurückmeldete, der Systemspeicher ging unterwegs auf 10 MB herunter... in einem 2 GB-System mit Windows. Dabei ging die Prozessorauslastung auf 70 % hoch und die Festplatte lief sich tot... nun, so gehts jedenfalls nicht, da man nie weiss, was man so einlesen muß.
Im Gegensatz dazu brauchte "meine" Methode 3 Sekunden und der Rechner blieb cool. Besser, denke ich...
Kommentar von Struppi
Na gut, wenn du GB grosse Dateien ohne Newline Zeichen hast, dann ist das sicher richtig (wobei das Problem nicht an Perl liegt, sondern am OS). Bei mir kommen solche Dateien selten vor (im Grunde nie).
Realistischer ist z.b. das man die Anzahl der Zeilen in einem Logfile herausfinden möchte, also extrem viele Zeilen mit ca. 100 Zeichen.
Jetzt mach mal einen Benchmark, deine Lösung braucht bei 100.000 Zeilen braucht bei mir sechsmal so lange und je mehr Zeilen umso schlechter wird das Verhältnis.
Also man sollte sich vorher überlegen, ob man Dateien hat, die extrem lange Zeilen haben könnten, dann wäre deine Lösung optimal, bei normal langen Zeilen ist sie langsam.
Kommentar von Peter
finde den Blog gut, habe aber einen Fehler entdeckt in
"Wie viele Zeilen hat die Datei"
Statt:
$zeilenanzahl=@zeilen;
print "Die Datei hat $zeilen Zeilen";
muss es heißen:
$zeilenanzahl=@zeilen;
print "Die Datei hat $zeilenzahl Zeilen";