Artikel im Internet unter http://www.hidemail.de/blog/zeilen-einlesen-datei-anzahl.shtml.
Freitag, 1.6.2007, 11:55:37 Uhr

Bestimmte Anzahl von Zeilen aus Datei einlesen


Ein Problem beim Einlesen von Dateien kennen viele: Entweder man liest die Datei komplett in den Arbeitsspeicher ein und "verschleudert" so wertvolle Systemressourcen, oder man liest Zeilen nacheinander ein und hat dann das Problem, daß die jeweils nachfolgende zeile nicht da ist, wenn man sie braucht.

Ein Beispiel für das komplette Einlesen von Dateien

#Datei komplett einlesen
use strict;
open (my $IN,'<'.'1.txt');
my @array=(<$IN>);
close $IN;



Ein Beispiel für das zeilenweise Einlesen von Dateien

use strict;
open (my $IN,'<'.'1.txt');
my @array=();

while (<$IN>){
print "$_\n";

}
close $IN;



Wie stellt man es also an, wenn man zeilenweise, zum Beispiel immer vier Zeilen, einlesen möchte?
"Normalerweise" würde man es so machen

use strict;
open (my $IN,'<'.'1.txt');
my @array=(<$IN>);
close $IN;

for (my $i=0;$i<@array;$i+=4){
print join("\n",@array[$i..$i+3]);
print "\n\n";

}


Blöd eben, daß die komplette Datei einglesen werden muß. Bei einer Megabyte großen Datei kanns schon mal Probleme geben...


Ich hab mir da mal sowas einfallen lassen:

use strict;
open (my $IN,'<'.'1.txt');
my @array=();
my $cou=0;

while (<$IN>){
@array[$cou]=$_;
$cou++;
next if $cou <4;

# die vier Einträge verarbeiten
$cou=0;
print join("\n",@array);
print "\n\n";

} # while-Scheife


Was passiert hier?
Also, die Datei 1.txt wird wie gehabt geöffnet, das Array vorbereitet, und natürlich ein Counter, der immer von 0 bis 3 läuft. Dieser Counter bestimmt gleich, wann vier Werte eingelesen wurden.
Weiter im Script: In der while-Schleife wird per direkter zuweisung der Eintrag @array[$cou] auf $_ gesetzt. $_ ist die aktuelle Zeile aus der Textdatei.
Danach erhöhen wir den Counter um eins, und wenn er kleiner 4 ist (0 - 3 sind ja vier Werte) wird die nächste Zeile eingelesen.
Sollte der Counter = 4 sein, setzen wir ihn zurück auf 0 für den nächsten Durchlauf und haben die letzten 4 Werte im Array.

Auf diese Weise kann man zum Beispiel Datendateien mit Datensätzen auslesen, ohne sie vollständig einlesen zu müssen.

Zusatz

Aufgrund des kommentars von René Bäcker unten, der natürlich richtig ist, hier noch eine erweiterte Version, die zum Schluß noch die übrig gebliebenen Einträge ausgibt.

use strict;
open (my $IN,'<'.'1.txt');
my @array=();
my $cou=0;

while (<$IN>){
@array[$cou]=$_;
$cou++;
next if $cou <4;

# die vier Einträge verarbeiten
$cou=0;
print join("\n",@array);
print "\n\n";
@array=();
} # while-Scheife

print "Übrig gebliebene Einträge:\n ".join ("\n",@array) if $cou != 0;


Das Vorgehen ist wie bei dem Script vorher, wenn jedoch am Ende der Counter != 0 ist (also noch Zeilen eingelesen wurden, aber nicht die 4 erreicht haben), werden so die Übrigen ausgegeben.

Weiterer Zusatz

Aufgrund des Vorschlages von Struppi, unten in den Kommentaren, will ich natürlich auch seine Methode nochmal extra aufführen und erläutern.

Sein Code nochmal:

#! /usr/bin/perl -w
use strict;

my $tmp = '';
while (<DATA>) {
$tmp .= $_;
next if $. % 4;
print "$tmp", ( '-' x 50 ), "\n";
$tmp = '';
} # while-Scheife

print "Übrig gebliebene Einträge:\n$tmp" if $tmp;


Die Funktion ist natürlich die Gleiche, es werden immer 4 Zeilen eingelesen und danach ausgegeben. Natürlich würde man "im echten Leben" damit etwas sinvolles anstellen.

Wie funktioniert es?
Also, Datei wird geöffnet und in einer while-Schleife durchlaufen. So wie in meinem Beispiel auch.
Danach wird per Modulo 4 (%4) getestet, ob die aktuelle Zeilenzahl der einzulesenden Datei durch 4 teilbar ist, ohne Rest. $. gibt ja immer die aktuelle Zeilennummer der einzulesenden Datei an (siehe Vordefinierte Variablen). Falls die aktuelle Zeilennummer durch 4 teilbar ist, wird das Ergebnis ausgegeben, ansonsten der nächste Wert geholt.
Nun, funktionieren tuts auch, und das sogar ohne extra Zähler, wie in meinem Beispiel. Ob es performanter ist als mein Beispiel, kann ich jetzt nicht sagen, aber vermutlich ist Struppis Version einen Hauch schneller.

Wie man sieht, gibts eben in Perl immer mehrere Wege, ein Problem umzusetzen.

Danke nochmal für die aufmerksame Mitleserschaft!


Artikel im Internet unter http://www.hidemail.de/blog/zeilen-einlesen-datei-anzahl.shtml.