Hat jemand Tipps zur Optimierung dieses (teil)scriptes?
pehajo
- perl
0 Christian Kruse0 Klaus Mock0 AlexBausW0 AlexBausW
Hallo,
hab mir als perl-Beginner nachfolgendes script zusammengebastelt:
gegeben: Tabelle mit Datensaetzen getrennt durch pipe
Wenn eine Bedingung auf listenelement[2] zutrifft, soll das letzte Element
der Liste [5] um eins erhoeht werden (=counter) und die Liste in eine neue
Datei geschrieben werden.
$benutzer = "irgendwas";
open(DATEI, "/.....adressen.txt") die $!;
while(($line = <DATEI>))
{
chop($line);
@liste = split(//, $line);
if ($liste[2] eq $benutzer)
{
$counter = $liste[5];
$counter++;
pop(@liste);
push(@liste , $counter);
}
push(@listeneu , "$liste[0]$liste[1]$liste[2]$liste[3]$liste[4]$liste[5]\n");
}
close(DATEI);
open(DATEINEU, ">/......adressenneu.txt") die $!;
print DATEINEU @listeneu;
close(DATEINEU);
es funktioniert soweit.
Mich wuerde aber interessieren, wie man es hinsichtlich performance optimieren soll.
Mit performance meine ich Laufzeit und Speicherbedarf des Rechners, da die Adressdatei
ca. 1000 Zeilen hat (ca. 300kb)
Vielleicht streubt es ja die insider bei obigen code die Haare ;-)
Wuerde mich ueber Anregungen freuen, danke vorab!
pehao
Hi,
$benutzer = "irgendwas";
open(DATEI, "/.....adressen.txt") die $!;
@lst = grep(/$benutzer/,<DAT>);
close(DAT);
@neu = ();
foreach (@lst)
{
s/^((.+){4})([0-9]+)(.+)$/"$1".($3+1)."$4"/e;
push(@neu,$_);
}
open(DATEINEU, ">/......adressenneu.txt") die $!;
print DATEINEU @neu;
close(DATEINEU);
vielleicht so?
mfg
CK1
<img src="http://wwwtech.de/images/banner.jpg" alt="">
http://wwwtech.de/
http://wwwtech.de
Hi,
hm, ich hab die Erklärungen vergessen, sorry ;) (danke, Antje, für den Hinweis *g*)
$benutzer = "irgendwas";
open(DATEI, "/.....adressen.txt") die $!;
@zeilen = <DAT>;
@lst = grep(/$benutzer/,@zeilen);
close(DAT);
@neu = ();
foreach (@lst)
{
s/^((.+){4})([0-9]+)(.*)$/"$1".($3+1)."$4"/e;
push(@neu,$_);
}
push(@neu,grep(!/$benutzer/,@zeilen));
open(DATEINEU, ">/......adressenneu.txt") die $!;
print DATEINEU @neu;
close(DATEINEU);
Dadurch sparst du dir überflüssige Schleifendurchläufe, usw.
Solltest du nur eine Zeile darin haben, die matchen kann, dann kannst du das Script noch vereinfachen
und die foreach-Sache weglassen und den Ausdruck einfach auf das erste Element der gefilterten
Liste anwenden.
mfg
CK1
<img src="http://wwwtech.de/images/banner.jpg" alt="">
http://wwwtech.de/
http://wwwtech.de
Hallo,
@lst = grep(/$benutzer/,@zeilen);
[...]
push(@neu,grep(!/$benutzer/,@zeilen));
Zweimal die ganze Datei durchgreppen ist doch ziemlich aufwendig oder?
Außerdem, so wie Du es machst auch ziemlich gefährlich. Was ist, wenn $benutzer in anderen Zeilen irgendwoanders steht?
also, wenn schon, dann
@lst = grep(/(.+){2}$benutzer/,@zeilen);
oder?
Und dann nochmals eine ziemlich komplexe Regex zum Counter-Erhöhen.
Sieht zwar mächtig abgefahren aus, ob das eine Performancesteigerung bringt ist fraglich.
Nix für ungut
Klaus
Hi,
@lst = grep(/$benutzer/,@zeilen);
[...]
push(@neu,grep(!/$benutzer/,@zeilen));Zweimal die ganze Datei durchgreppen ist doch ziemlich aufwendig oder?
Naja, IMHO besser als die Liste durchzuschleifen.
Außerdem, so wie Du es machst auch ziemlich gefährlich. Was ist, wenn $benutzer in anderen Zeilen
irgendwoanders steht?
Das ist bei ihm auch nicht vorgesehen ;) da geht es auch _immer_ um $liste[2] ;) ergo ist es ein festes
Datenformat.
@lst = grep(/(.+){2}$benutzer/,@zeilen);
oder?
Wozu das?
grep(/$benutzer/,@zeilen)
durchsucht die gesamte Zeile nach einem String.
$string =~ /bla/;
durchsucht doch auch den gesamten String nach dem Pattern (in dem Fall "bla").
Und dann nochmals eine ziemlich komplexe Regex zum Counter-Erhöhen.
Komm, Komm, so komplex ist das nicht ;) und IMHO schneller als split() und dann wieder join()
Sieht zwar mächtig abgefahren aus,
nicht wirklich ;)
ob das eine Performancesteigerung bringt ist fraglich.
eben schon - es werden wirklich nur die gesuchten Zeilen bearbeitet und es wird nicht gesplittet und dann
wieder gejoint.
Nix für ungut
eh nicht ;)
mfg
CK1
Hallo,
@lst = grep(/(.+){2}$benutzer/,@zeilen);
oder?
Wozu das?
grep(/$benutzer/,@zeilen)
durchsucht die gesamte Zeile nach einem String.
$string =~ /bla/;
durchsucht doch auch den gesamten String nach dem Pattern (in dem Fall "bla").
Das ist ja das Problem.
Zeile1:StefanMünzStefan....
Zeile2:StefanHuberstef....
Die Zeile darf nur gefunden werden, wenn nach dem zweiten und vor dem dritten $benutzer drin steht.
grep(/Stefan/,@zeilen);
würde aber beide Zeilen finden, was jedoch nicht richtig ist.
Grüße
Klaus
Hallo,
Vielleicht streubt es ja die insider bei obigen code die Haare ;-)
Ganz so schlimm ist es nicht, denk ich.
Bis auf die wirklich unnötige Verwendung von $counter und dem pop/push auf @liste ist alles eigentlich nur Geschmacksfrage.
Ob es an der Performance grundlegend was ändert, weiß ich nicht, ein paar Quentchen vielleicht. Wenn Dein Script allerdings weit weg von der gewünschten Geschwindigkeit, dann ist der von Dir eingeschlagene Weg nochmals zu überdenken.
Solange aber die Menge nicht ins uferlose steigt, glaube ich bist Du noch im grünen Bereich.
Mir ist gerade noch aufgefallen, daß Du sowieso in eine neue Datei schreibst. Dann könntest Du alles gleich in einem Rutsch machen und auf das Array @listeneu verzichten:
open(IN,"adressen.txt") or die "nix lesen";
open(OUT,">adressenneu.txt") or die "nix schreiben";
while(<IN>)
{
chomp;
my(@liste) = split('',$_);
$liste[5]++ if $liste[2] eq $benutzer;
print OUT join('',@liste),"\n";
}
close(IN);
close(OUT);
Grüße
Klaus
Hallo,
vielen Dank fuer Eure Tipps! Haette ich nicht gedacht, dass Ihr Euch so reinkniet ;-)
Die Loesung von Klaus ist mir sympathisch (weil ich sie halt raffe)
{
chomp;
my(@liste) = split('',$_);
$liste[5]++ if $liste[2] eq $benutzer;
print OUT join('',@liste),"\n";
}
close(IN);
close(OUT);
Hatte ich mich zu ungenau ausgedrueckt?
liste[2] ist wirklich ein fixer Platz und kann auch "Stefan" oder "stef" enthalten.
eine regexp scheidet also aus. Klaus hat es richtig erkannt.
Der Wert kann aber nur einmal in der datei vorkommen, da es ein Benutzername ist.
Ausserdem schreibe ich die Daten wieder in die gleiche Datei zurueck (sorry, dass mit
neuer Datei war aus meinen Testlaeufen). Leider unterstuetzt der Server (t-mart) keine
system(), sodass ich keine temp.dat erstellen kann und die dann einfach rueberkopieren
kann. Ich muss also: oeffnen -lesen - schliessen -wiederoeffnen - schreiben-schliessen
Vielen Dank nochmal, werde mir jetzt mal das join "reinziehen" ;-)
Gruesse
pehao
Hallo,
vielen Dank fuer Eure Tipps! Haette ich nicht gedacht, dass Ihr Euch so reinkniet ;-)
Na ja, wo sich drei Leute treffen, da gibts halt mind. vier Meinungen ;-)
Und Du siehst, daß es viele Wege gibt, die zum Ziel führen können. Das ist ja eine der schönsten Seiten von Perl.
[...] Leider unterstuetzt der Server (t-mart) keine
system(), sodass ich keine temp.dat erstellen kann und die dann einfach rueberkopieren
??? aber es gibt ja noch rename und unlink.
Die Perl-Doku solltest Du unbedingt so auf Deinem Rechner einrichten, daß Du schnell darauf Zugriff hast. Es zahlt sich wirklich aus, dariin zu stöbern.
Vielen Dank nochmal, werde mir jetzt mal das join "reinziehen" ;-)
Aber pass auf, daß Dich keiner dabei erwischt ;-)
Grüße
Klaus
_Servus_ Oida,
[...] Leider unterstuetzt der Server (t-mart) keine
system(), sodass ich keine temp.dat erstellen kann und die dann einfach rueberkopieren
??? aber es gibt ja noch rename und unlink.
muass i ausprobiern. Aba bei de blädn T-Offline-Server woas ma ja nie, wos do no ois zum
Vorschein kimmt.
Die Perl-Doku solltest Du unbedingt so auf Deinem Rechner einrichten, daß Du schnell darauf Zugriff hast. Es zahlt sich wirklich aus, dariin zu stöbern.
Wois scho, aber sovui Bam, da siegst ja den Woid nimma ;-)
mersse noamoi,
pehao
...das war?
[ ] der 2001. Dialekt
[ ] aus dem Weisswurst-Terrain
[ ] Schmarrn
Hallo pehajo,
Hatte ich mich zu ungenau ausgedrueckt?
Ich denke nicht wirklich, da ja die Variablenbezeichnung $benutzer nahelegt, worum es geht. ;-)
liste[2] ist wirklich ein fixer Platz und kann auch "Stefan" oder "stef" enthalten.
eine regexp scheidet also aus. Klaus hat es richtig erkannt.
Da unterschätzt Du aber die Möglichkeiten der RegEx mit beliebigem Text umzugehen. Und Dein File ist nichts anderes.
Ein RegEx scheidet also in diesem Fall bestimmt nicht aus. Aber das merkst Du, wenn Du mal eine Zeitlang in "perldoc perlre" gestöbert hast ;-)
Der Wert kann aber nur einmal in der datei vorkommen, da es ein Benutzername ist.
Genau den Fall kann auch eine RegEx wie:
^((?:[^|]*|){2}$nutzer|(?:[^|]*|){2})(\d*?)$
abdecken. Sie matched nur ganze Zeilen, in denen genau zwischen 2. und 3. | $nutzer vorkommt (natürlich darf $nutzer kein | enthalten, weil es auch bei der split()-Version Schwierigkeiten bereitet :-). Die nach dem letzten | gefundene Zahl wird bei der Ersetzung um eins erhöht. Also funktioniert eine RegEx in Verbindung mit Suchen ersetzen wunderbar.
Ausserdem schreibe ich die Daten wieder in die gleiche Datei zurueck (sorry, dass mit
neuer Datei war aus meinen Testlaeufen). Leider unterstuetzt der Server (t-mart) keine
system(), sodass ich keine temp.dat erstellen kann und die dann einfach rueberkopieren
kann. Ich muss also: oeffnen -lesen - schliessen -wiederoeffnen - schreiben-schliessen
Eine Funktion, die eine Liste zurückgibt, kann natürlich auch in einer Liste gespeichert werden, statt sie gleich auszudrucken.
@neu = map {...} @alt;
Wie Du siehst, geht mit Perl fast alles ;-)
Gruß AlexBausW
Please visit my SELFvisitingcard @ http://www.atomic-eggs.com/selfspezial/daten/150.html
Hallo pehajo,
Klaus und Christian haben ja schon einiges optimiert und aufgeklärt.
Bleibt mir nur noch, einen "Einzeiler" zu schreiben, der auch das macht was Du willst ;-)
my $nutzer = "MeinName";
open (IN, "alt.txt") or die $!;
open (OUT, ">neut.txt") or die $!;
# map() evaluert den Code im Block für alle Elemente der Liste, und gibt den letzten "Ausdruck" zurück,
print OUT map { s/^((?:.*?){2}$nutzer.*?)(\d*?)$/$1.($2 + 1)/e; $_; } <IN>;
# die RegEx matched, wenn $nutzer an 3. Stelle zwischen und steht.
# Dann wird es durch das Ergebnis der ersten Klammer ersetzt,
# und das Ergebnis der zweiten Klammer um eins erhöht.
close OUT;
close IN;
Ich hoffe es ist nicht zu kryptisch, aber es funktioniert imho einwandfrei ;-)
Gruß AlexBausW
P.S.: Einzeiler sind ein kleines Hobby von mir ;-)
Please visit my SELFvisitingcard @ http://www.atomic-eggs.com/selfspezial/daten/150.html
Hallo me,
print OUT map { s/^((?:.*?){2}$nutzer.*?)(\d*?)$/$1.($2 + 1)/e; $_; } <IN>;
Da hat sich doch ein kleiner Bug eingeschlichen, den Du aber sicherlich bald entdeckt hättest. Dein @liste besteht ja aus sechs Elementen. Also muß die obige Zeile korrekt lauten:
print OUT map { s/^((?:.*?){2}$nutzer(?:.*?){2})(\d*?)$/$1.($2 + 1)/e; $_; } <IN>;
Die Fragezeichen hinter * kannst Du imho auch weglassen, da afaik Perl die an den vorgegebenen Stellen matchen wird (Versuch, immer einen Treffer zu finden, hat imho Vorrang vor der Gierigkeit von *).
Gruß AlexBausW
Please visit my SELFvisitingcard @ http://www.atomic-eggs.com/selfspezial/daten/150.html
hi ho
print OUT map { s/^((?:.*?){2}$nutzer(?:.*?){2})(\d*?)$/$1.($2 + 1)/e; $_; } <IN>;
Die Fragezeichen hinter * kannst Du imho auch weglassen, da afaik Perl die an den vorgegebenen Stellen matchen wird (Versuch, immer einen Treffer zu finden, hat imho Vorrang vor der Gierigkeit von *).
ja, aber es wird ne menge backtracking erzeugt, wenn, dann ist es besser so:
print OUT map { s/^((?:[^|]*|){2}$nutzer|(?:[^|]*|){2})(\d*?)$/$1.($2 + 1)/e; $_; } <IN>;
(ich hab die pipes fuers archiv auch gleich mal eben maskiert... :-)
cua
n.d.p.
Hallo,
print OUT map { s/^((?:[^]*){2}$nutzer(?:[^]*){2})(\d*?)$/$1.($2 + 1)/e; $_; } <IN>;
Ich bin ja nicht so der Freund von Einzeilern, aber gibts da nicht ein Problem mit der Klammern und den daraus resultierenden 'Matchingergebnissen' ?
Ich denke $2 ist das Ergebnis von '(?:[^]*)' und $4 von '(\d*?)'.
Grüße
Klaus
Hallo Klaus,
print OUT map { s/^((?:[^|]*|){2}$nutzer|(?:[^|]*|){2})(\d*?)$/$1.($2 + 1)/e; $_; } <IN>;
Ich bin ja nicht so der Freund von Einzeilern, aber gibts da nicht ein Problem mit der Klammern und den daraus resultierenden 'Matchingergebnissen' ?
Ich denke $2 ist das Ergebnis von '(?:[^]*)' und $4 von '(\d*?)'.
(?: verhindert, daß das Ergebnis des Matchings der Klammer in $1-$9 abgelegt wird. Natürlich könnte man auch die Klammern hochzählen, aber wenn man nur die äußeren Klammern braucht ist es ihmo unnötig, und imho weniger übersichtlich.
Gruß AlexBausW
Please visit my SELFvisitingcard @ http://www.atomic-eggs.com/selfspezial/daten/150.html