On Mon, 23 Jun 2003 at 03:00 (+0200), David Haller wrote:
On Mon, 23 Jun 2003, Jan Trippler wrote:
# Datei lesen die "usage: $0 Dateiname\n" unless defined $ARGV[0]; open DATEI, "$ARGV[0]" or die "Fehler beim Oeffnen der Datei ($!)\n";
Die Datei sollte man IMO explizit "readonly" oeffnen...
my @zeilen = <DATEI>; close DATEI;
*AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA*
Das ist der gleiche Fehler wie von grepmail. Versuch das mal mit ner Datei von ein paar 100 MB!!! Stehen lassen kann man das "open".
Ich habe hier einfach die Vorlage übernommen - ich benutze dieses Konstrukt auch oft, wenn ich weiss, dass die Dateien überschaubar sind. Der Vorteil: Du hast das Array immer bei der Hand, wenn Du es nochmal brauchst, ohne den Dateipointer zurücksetzen zu müssen oder die Datei neu einlesen zu müssen. Ist einfach bequem und ich werde es (mit der o. g. Einschränkung) auch weiterhin gern nutzen ;-) Für mehrere 100 MB fehlte mir die Geduld, aber ich habe mal eine Datei mit 150.000 Datensätzen generiert (meine 3 Testsätze einfach 50.000 mal aneinandergehängt, ergab ca. 19 MB): Die Testsätze: <schnipp> jan@k500:~/tmp> cat kunden Kdnr: 1211 Name: Schmitz, Peter Firma: Schmitz GmbH Beschreibung: Projekt XY, Netzwerkverkabelung Telefon: 0123 45678 Zusatz: Kunde will Angebot für VPN Kdnr: 1219 Name: Müller, Franz Firma: Müller & Söhne GbR Beschreibung: Projekt AB, Datensicherung Beschreibung: Datensicherung Windows NT-Server Telefon: 0123 45678 Kdnr: 1239 Name: Miller, Granz Firma: Miller & Söhne GbR Telefon: 0123 45678 <schnapp> Meine Variante: jan@k500:~/tmp> time ./out.pl >kunden.html real 7m32.256s user 5m45.190s sys 0m6.340s Deine Variante: jan@k500:~/tmp> time ./out.pl >kunden2.html real 7m25.972s user 5m54.390s sys 0m5.440s Hm, diese Differenz ist für mich vernachlässigbar.
# meine Variablen my %daten = (); # der Hash fuer die Werte und Keys eines Datensatzes my @spalten = ("Kdnr", "Name", "Firma", "Beschreibung", "Telefon", "Zusatz");
# Tabellenheader print "<table width='100%' border='1'>\n"; print "<tr>\n"; foreach (@spalten) { printf "<th>%s</th>\n", $_; } print "</tr>\n";
foreach (@zeilen) { # Zeilen abklappern, Zeile wird in $_ abgelegt
*ARGH* Hier wird's dann wirklich doof, wenn man sowieso zeilenweise liest, kann man gleich die Datei hernehmen.
while(<DATEI>) { # Zeilen abklappern, Zeile wird in $_ abgelegt
(ja, der Kommentar stimmt so!)
Es wird nur dann doof, wenn Du sie nicht in ein Array gelesen hast. Wenn Du unbedingt zeilenweise lesen willst, kannst Du natürlich nicht über ein Array iterieren.
chomp; # \n wegwerfen next if /^ *$/; # leere Zeilen wegschmeissen
Wuerde ich andersrum schreiben, bei leeren Zeilen faellt dann der ueberfluessige Aufruf von 'chomp' weg:
next if /^\s*$/; # leere Zeilen wegschmeissen chomp; # \n wegwerfen
Jau, spart einen Funktionsaufruf.
if (defined ($daten{'Kdnr'}) && /^Kdnr/) { # $_ faengt mit KdNr an print "<tr>\n"; # Tabellenzeile foreach my $out (@spalten) { # Spalten abklappern if (defined $daten{$out}) { # Key vorhanden? printf "<td>%s</td>\n", $daten{$out}; # Ja: Wert schreiben undef ($daten{$out}); # Hash-Key wegschmeissen
Ich wuerde eher jew. beim Auftauchen von 'Kdnr' das gesamte hash reinitialisieren:
if (defined ($daten{'Kdnr'}) && /^Kdnr/) { # $_ faengt mit KdNr an print "<tr>\n"; # Tabellenzeile foreach my $out (@spalten) { # Spalten abklappern if (defined $daten{$out}) { # Key vorhanden? printf "<td>%s</td>\n", $daten{$out}; # Ja: Wert schreiben } else { print "<td> </td>\n"; # Nein: Leerzeichen } } print "</tr>\n"; # Ende Tabellenzeile %daten = (); # hash reinitialisieren }
## in $_ steht immer noch das neue Kdnr: ...
Was ist der Unterschied? Ich initialisiere zwar nicht das ganze Array, weil ich nur alle (in der Tabelle benutzten) Keys plattmache. Der Effekt ist aber der gleiche. Ich weiss nicht, wie Perl das intern handhabt und was performanter ist. Nachtrag: Die oberen beiden Tests liefen mit Deiner Variante der Initialisierung. Aus Neugier habe ich mal meine ursprüngliche Version laufen lassen (undef der einzelnen Keys): jan@k500:~/tmp> time ./out.pl >kunden3.html real 6m28.514s user 5m20.590s sys 0m5.220s Und der Vollständigkeit halber mit zeilenweisem Lesen: jan@k500:~/tmp> time ./out.pl >kunden4.html real 6m54.461s user 5m36.530s sys 0m5.420s Ich kann mir die einzelnen Abweichungen auch nicht 100% erklären (ich bin ja nicht die SPEC und habe die Dinger einfach nacheinander laufen lassen - also keine klinisch reine Testumgebung ;-), aber IMHO zeigt das, dass die Initialisierung einzelner Keys deutlich fixer ist, als die des gesamten Hash (immerhin ca. 15%).
my ($key, $value) = split /:/; # neue Zeile aufspalten
Die Deklaration wuerde ich aus der Schleife rausnehmen, das ist nicht noetig. Also das 'my ($key, $value)' vor das while und dann hier nur noch:
($key, $value) = split /:/; # neue Zeile aufspalten
Warum? Mit einer lokalen Deklaration bin ich sicher, dass die Variablen nirgends sonst irrtümlich benutzt werden, da weist mich perl schon drauf hin. Du führst unnötig global gültige Variablen ein, das ist IMHO ein Weg, der Fehler provoziert. Global definierte Variablen verführen immer dazu, sie einfach noch mal zu benutzen (oder aus Versehen noch mal zu benutzen - perl warnt ja dann nicht mehr), und dann wird es deutlich aufwändiger, Seiteneffekte zu vermeiden.
Ich denke, fuer dieses kleine scripterl sind die globalen Variablen angebracht...
s. o. Jan