Letzte Zeilen einer sehr großen Datei auslesen
coolblue
- perl
2 Christian Kruse1 Rolf Rost
Hallo,
ich möchte gerne die letzten 160 Zeilen einer sehr großen Datei auslesen. Die Datei ist ca. 140 MB groß. Bisher bin ich wie folgt vorgegangen:
-----------------------------------------------
open(DATEI,"<$datei") or die "Can't open $datei!\n";
my $i;
for($i=0 ; <DATEI> ; $i++) {} #Zeilen zählen
close DATEI;
$i-=160; #die besagten 160 Zeilen abziehen
my $n=0;
my @data;
open(DATEI,"<$datei") or die "Can't open $datei!\n";
for(my $s=0 ; <DATEI> ; $s++) {
if($s >= $i) { #Zeilen ab $i-160 einlesen
$data[$n]="$_";
$n++;
}
}
close DATEI;
-----------------------------------------------
Gibt es eine bessere Möglichkeit, die vorallem effizienter ist? Ich habe zuerst an tail -160 gedacht, möchte aber keine Subshell aufmachen. Gibt es hierfür Perl interne Mittel?
Wäre
for(my $s=0 ; <DATEI> ; $s++) {
next if $s < $i;
$data[$n]="$_";
$n++;
}
das Gleiche, nur eine andere Schreibweise, oder auf jeden Fall schon mal besser als das obere Beispiel?
Viele Grüße,
coolblue
你好 coolblue,
open(DATEI,"<$datei") or die "Can't open $datei!\n";
my $i;
for($i=0 ; <DATEI> ; $i++) {} #Zeilen zählenclose DATEI;
$i-=160; #die besagten 160 Zeilen abziehen
my $n=0;
my @data;open(DATEI,"<$datei") or die "Can't open $datei!\n";
for(my $s=0 ; <DATEI> ; $s++) {
if($s >= $i) { #Zeilen ab $i-160 einlesen
$data[$n]="$_";
$n++;
}
}close DATEI;
Buarghs. Das ist wirklich grausam, was du da tust, du liest die Datei
voellig unnoetigerweise zweimal aus.
Gibt es eine bessere Möglichkeit, die vorallem effizienter ist?
Im Grunde nicht. Du musst in jedem Fall alles von der Datei auslesen.
Ich habe zuerst an tail -160 gedacht, [...]
tail macht auch nichts anderes als alles auszulesen und nur den relevanten
Teil auszugeben ;-)
Wäre
for(my $s=0 ; <DATEI> ; $s++) {
next if $s < $i;
$data[$n]="$_";
$n++;
}das Gleiche, nur eine andere Schreibweise, oder auf jeden Fall schon
mal besser als das obere Beispiel?
Wenn du auch die Zaehlschleife zum Zaehlen der Zeilen einer Datei
weglaesst, dann ist das auf jeden Fall sinnvoller.
再见,
CK
Hallo Christian,
Buarghs. Das ist wirklich grausam, was du da tust, du liest die Datei
voellig unnoetigerweise zweimal aus.
das verstehe ich nicht! Wie soll ich denn sonst herausfinden, wieviele Zeilen die Datei hat? Die Zeilenanzahl variiert ständig.
Ich hatte zuallererst an folgendes gedacht:
@in=<DATEI>;
aber das wäre wohl total mieß für den Speicher.
Wenn du auch die Zaehlschleife zum Zaehlen der Zeilen einer Datei
weglaesst, dann ist das auf jeden Fall sinnvoller.
wie meinst du das? Wenn ich Zählerschleife weglasse, dann weiß ich doch nicht, wann ich an Zeile $i-160 angekommen bin.
Oh je, i need help!
Viele Grüße,
coolblue
你好 coolblue,
Buarghs. Das ist wirklich grausam, was du da tust, du liest die Datei
voellig unnoetigerweise zweimal aus.das verstehe ich nicht! Wie soll ich denn sonst herausfinden, wieviele
Zeilen die Datei hat? Die Zeilenanzahl variiert ständig.
Das musst du doch gar nicht wissen. Warum interessiert dich das?
Wenn du auch die Zaehlschleife zum Zaehlen der Zeilen einer Datei
weglaesst, dann ist das auf jeden Fall sinnvoller.wie meinst du das? Wenn ich Zählerschleife weglasse, dann weiß ich doch
nicht, wann ich an Zeile $i-160 angekommen bin.
War etwas unueberlegt von mir, zugegeben. Aber egal, speichere die letzten
160 Zeilen und lies jedesmal, wenn du eine neue Zeile einliest die aelteste
gespeicherte Zeile weg.
再见,
CK
Das musst du doch gar nicht wissen. Warum interessiert dich das?
Weil ich mit den Daten der letzten 160 Zeilen einen Graphen generiere (GD::Graph::lines).
Viele Grüße,
coolblue
你好 coolblue,
Das musst du doch gar nicht wissen. Warum interessiert dich das?
Weil ich mit den Daten der letzten 160 Zeilen einen Graphen generiere
(GD::Graph::lines).
Und warum musst du wissen, wieviele Zeilen die Datei hat?
再见,
CK
Und warum musst du wissen, wieviele Zeilen die Datei hat?
hmmmm...
Beispiel:
Eine Datei wird alle paar Sekunden um eine oder mehrere Zeilen erweitert. Nun möchte ich die letzten 160 Zeilen aus dieser Datei lesen, weil die letzten 160 Zeilen die aktuellsten sind. Jetzt hat die Datei zum Beispiel 15768 Zeilen, davon möchte ich die letzten 160 lesen.
Also gehe ich wie folgt vor: for($i=0 ; <DATEI> ; $i++) {}
Nun weiß ich, wieviele Zeilen die Datei hat und möchte die letzten 160 davon in einem Array einlesen. Das Array übergebe ich dann meinem Graphen.
$i-=160; # 15768-=160 == 15607
Jetzt lese ich nochmal die Datei:
for($s=0 ; <DATEI> ; $s++) {
next if $s <= $i; # übersetzt: nächste zeile wenn $s kleiner-gleich 15607 ist
$data[$n]=$_; # wenn aber $s größer ist als 15607, lese ein
$n++; # index+1
}
Auf diese Weise lese ich die Zeilen 15607 bis 15768 ein, also die letzten 160 Zeilen der Datei. Hätte ich nicht vorher die Datei einmal durchgezählt, wüßte ich nicht, wann die lettzen 160 Zeilen beginnen.
Viele Grüße,
coolblue
你好 coolblue,
$i-=160; # 15768-=160 == 15607
Jetzt lese ich nochmal die Datei:
for($s=0 ; <DATEI> ; $s++) {
next if $s <= $i; # übersetzt: nächste zeile wenn $s kleiner-gleich 15607 ist
$data[$n]=$_; # wenn aber $s größer ist als 15607, lese ein
$n++; # index+1
}Auf diese Weise lese ich die Zeilen 15607 bis 15768 ein, also die
letzten 160 Zeilen der Datei. Hätte ich nicht vorher die Datei
einmal durchgezählt, wüßte ich nicht, wann die lettzen 160 Zeilen
beginnen.
Ja, und ich habe dir doch schon erklaert, wie du das besser machen
koenntest. Hier mal Beispiel-Code, damit du es besser verstehst:
open DAT,'<test.txt';
my $i = 0;
my $line = '';
my @lines = ();
my $wanted_num = 4;
for($i=0;$line=<DAT>;++$i) {
$lines[$i%4] = $line;
}
close DAT;
print foreach(@lines);
Damit lese ich aus jeder Datei die letzten 4 Zeilen heraus.
再见,
CK
Hi,
for($i=0;$line=<DAT>;++$i) {
$lines[$i%4] = $line;
}
print foreach(@lines);
Damit lese ich aus jeder Datei die letzten 4 Zeilen heraus.
gibst sie aber nicht immer in der Reihenfolge aus, in der sie in der Datei stehen.
cu,
Andreas
你好 MudGuard,
Hi,
for($i=0;$line=<DAT>;++$i) {
$lines[$i%4] = $line;
}
print foreach(@lines);
Damit lese ich aus jeder Datei die letzten 4 Zeilen heraus.gibst sie aber nicht immer in der Reihenfolge aus, in der sie in der
Datei stehen.
Nein :) Aber da mir die Anzahl der Zeilen bekannt ist, kann ich die
Reihenfolge problemlos feststellen wenn es notwendig ist.
再见,
CK
Hi,
Im Grunde nicht. Du musst in jedem Fall alles von der Datei auslesen.
Wirklich?
Nur mal so als Idee:
per Seek auf die letzten z.B. 10K positionieren.
Einlesen bis zum Dateiende.
Gucken, ob das gereicht hat, um 160 Zeilen zu erwischen.
Falls ja, die letzten 160 Zeilen benutzen.
Falls nein, per Seek auf die vorletzten 10K positionieren.
Nochmal 10K einlesen (etwas aufpassen mit der Nahtstelle zwischen den 2 Blöcken wg. Zeilen).
Gucken, ob jetzt genug Zeilen eingelesen sind,
falls ja, diese benutzen,
falls nein, nochmal 10K holen bis entweder die 160 Zeilen erreicht sind oder der Dateianfang.
cu,
Andreas
你好 MudGuard,
Im Grunde nicht. Du musst in jedem Fall alles von der Datei auslesen.
Wirklich?
Nur mal so als Idee:
[...]
Das setzt genaue Kenntnis der vorliegenden Daten vorraus (deine Methode
waere toedlich, wenn z. B. 130MB auf die letzten 160 Zeilen verteilt sind),
daraus laesst sich kein allgemeingueltig schneller Algorithmus
formulieren; den Gedanken hatte ich auch, habe ihn aber nicht geaeussert,
weil ich die Daten des OP nicht kenne.
再见,
CK
Hallo Andreas,
Nur mal so als Idee:
per Seek auf die letzten z.B. 10K positionieren.
Einlesen bis zum Dateiende.
Gucken, ob das gereicht hat, um 160 Zeilen zu erwischen.
Falls ja, die letzten 160 Zeilen benutzen.
Falls nein, per Seek auf die vorletzten 10K positionieren.
Nochmal 10K einlesen (etwas aufpassen mit der Nahtstelle zwischen den 2 Blöcken wg. Zeilen).
Gucken, ob jetzt genug Zeilen eingelesen sind,
falls ja, diese benutzen,
falls nein, nochmal 10K holen bis entweder die 160 Zeilen erreicht sind oder der Dateianfang.
Das mag hinhauen! Ich lese auch gerne 50k aus, ist noch immer erheblich weniger als 140 MB.
Hatte von seek bisher nichts gewußt, werde mich aber mal einlesen.
Wie schauts denn mit meinem Beispiel für kleinere Dateien aus. Wäre das so ok?
Viele Grüße,
coolblue
Moin,
hier noch eine Variante mit DB_File, damit wird die Datei an ein array gebunden und liegt in ihrer Gesamtgröße nicht im Speicher 'rum:
##################################################################
use DB_File;
use strict;
tie @in, "DB_File", $filename, O_RDWR|O_CREAT, 0644, $DB_RECNO or die $!;
print scalar @in, " Zeilen hat die Datei, untenstehend die letzten 20:\n";
for(-20..-1){ print "$in[$_]\n" }
untie @in;
##################################################################
Gruss, Rolf