Perl: regex: alle Vorkommen finden

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
sentolacco
Beiträge: 60
Registriert: 19.10.2006 21:26:39

Perl: regex: alle Vorkommen finden

Beitrag von sentolacco » 03.01.2007 03:39:29

Hallo,

bin grad dabei mich mit Perl und Regex auseinanderzusetzen.

Code: Alles auswählen

$code =~ /(<input.*>)/i;
print $1."\n";
in $code befindet sich der quellcode einer htmlseite. in dieser seite befindet siche ein <form> tag mit mehreren <input ... > feldern. ich möchte mir all diese anzeigen lassen.

das problem ist, dass ich nur das erste <input .. > angezeigt bekomme.

auch wenn ich hinter das i (vorletztes zeichen in der ersten zeile) noch ein g setzte wird mir nur ein <input .. > angezeigt.

was mache ich falsch?

MfG
sentolacco

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 03.01.2007 08:45:22

Code: Alles auswählen

gms@gms4:~$ cat x.html
bla0<input val1>bla1<input val2>bla2<input val3>bla3
gms@gms4:~$
gms@gms4:~$ perl -e '$/=">"; while(<>) {print $1,"\n" if /(<input.*>)/i;}' x.html
<input val1>
<input val2>
<input val3>
mit

Code: Alles auswählen

$/=">"
wird der "Input Record Separator" umgesetzt


Gruß
gms

nepos
Beiträge: 5238
Registriert: 05.01.2005 10:08:12

Beitrag von nepos » 03.01.2007 09:39:08

Oder du benutzt eines der vielen Parser-Module von Cpan. HTML mit Regexes zu parsen ist nicht immer so einfach und oft kommt es da zu netten Fehlern. Die Module sollten da sauberer arbeiten.

Benutzeravatar
Webermaster
Beiträge: 93
Registriert: 27.12.2002 11:44:32

Beitrag von Webermaster » 03.01.2007 10:05:21

Versuchs mal mit einem:

Code: Alles auswählen

while ($code =~ /(\<input.*?\>)/gi) {
	print "$1\n";
}
Gruss Alex

sentolacco
Beiträge: 60
Registriert: 19.10.2006 21:26:39

Beitrag von sentolacco » 03.01.2007 19:48:57

erstmal danke für eure antworten!

@qms: deine lösung konnte ich erstmal nicht umsetzen, weil ich bis jetzt noch nicht direkt auf der kommandozeile perl programmiert habe. hast du da irgendeinen link zu nem tut? ausserdem bin ich auf der suche nach dem "Input Record Separator" nicht wirklich fündig geworden...

@nepos: das ist ein guter tipp, danke dir, werde ich mal testen.

@webermaster: deine lösung funktioniert gut. ich habe allerdings noch zwei kleine (form)fragen:
1. warum setzt du "\" vor "<" und ">" ? es funktioniert auch ohne und ich habe im selfhtml-perl-tut nicht erkennen können, dass die spitzen klammern einen speziellen zweck in perl haben. täusche ich mich da?
2. was ist der unterschied zwischen deiner schreibweise print "$1\n"; und meiner schreibweise print $1."\n"? perforamnce? die einsparung eines chars? stil?

MfG
sentolacco

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 03.01.2007 21:21:06

ein Tutorial für Perl One-Liner: http://sial.org/howto/perl/one-liner/
sentolacco hat geschrieben: 1. warum setzt du "" vor "<" und ">" ? es funktioniert auch ohne und ich habe im selfhtml-perl-tut nicht erkennen können, dass die spitzen klammern einen speziellen zweck in perl haben. täusche ich mich da?
im zweifelsfall escape ich auch
sentolacco hat geschrieben: 2. was ist der unterschied zwischen deiner schreibweise print "$1\n"; und meiner schreibweise print $1."\n"? perforamnce? die einsparung eines chars? stil?
"Webmasters" schreibweise ist "übersichtlicher", allerdings muß in der Zeichenkette das "$1" ersetzt werden.
Deine Schreibweise sollte theoretisch etwas performanter sein, weil hier die Zeichenketten nur concateniert werden.
Meine Schreibweise wäre dann allerdings noch performanter: print $1,"\n"; (beachte Beistrich statt dem Punkt), weil hier weder ersetzt noch concateniert werden muß

Gruß
gms

ToPeG
Beiträge: 437
Registriert: 14.04.2004 00:42:06

Beitrag von ToPeG » 03.01.2007 21:27:10

Das quoten von "<" ">" ist nur in einzelfällen nötig (bei bestimmten RegExp-Konstrukten) normalerweise ist das unnötig.

Wie du sicher weißt werden alle Steuezeichen (wie \n \t $variablenname) in doppelten Anführungszeichen (") geparst, das kostet Zeit, aber auch da zusammenfügen von strings (wie 'test'."\n") kostst Zeit.
Im allgemeien ist "$var\n" schneller, außer man hat Konstrukte wie: "$welt->{baum}->{ast}->[3]->{blatt}->[75]->farbe()\n" an sowas bricht sich der Parser echt einen ab(und nicht selten geht es schief). Da iat das Ausgliedern der Varaiable besser.
Der Parser ist auch nicht allwissend. So kann er sowas nicht richtig Interpertien:

Code: Alles auswählen

$VAR='Haus';
print "$VARmann\n"
Aber imm allgemeinen ist es Geschmackssache wie man es schreibt.
Aber es lohnt sich immer den Parser weg zu lassen wenn man ihn nicht braucht.
So ist erste Script doch ganz klein wenig schneller:

Code: Alles auswählen

#!/usr/bin/perl
my $n="\n";
print 'Das ist ein Test on \n auch wirklich \x12 ist oder was $$ Anderers $$'.$n for(0..1000);
als das Zweite:

Code: Alles auswählen

#!/usr/bin/perl
print "Das ist ein Test ob \\n auch wirklich \\x12 ist oder was \$\$ Anderers \$\$\n" for(0..1000);
obwohl beide das selbe Ausgeben. Bei umfangreieren Ausgaben in Datein, oder der Konsole kann sich das wirklich bemerkbar machen.
Zuletzt geändert von ToPeG am 03.01.2007 21:43:29, insgesamt 1-mal geändert.

Benutzeravatar
Webermaster
Beiträge: 93
Registriert: 27.12.2002 11:44:32

Beitrag von Webermaster » 03.01.2007 21:30:18

1. Übervorsichtigkeit meinerseits. Ich hab nicht alle Regex-Zeichen im Kopf und mache manchmal ein \ zuviel.
2. Ich hab es so gelernt und find es lesbar, keine Ahnung was performanter ist. Bei komplizierten Variablen (bsp. Hashes von Arrays) mache ich zum vereinfachen doch Unterteilungen mit .

Gruss Alex

ToPeG
Beiträge: 437
Registriert: 14.04.2004 00:42:06

Beitrag von ToPeG » 03.01.2007 21:45:13

Ich habe gerade ieinen kleinen Test gemacht:

Code: Alles auswählen

#!/usr/bin/perl
use strict;
use warnings;

use Benchmark qw/cmpthese/;

my $var='TEST';
my $zahl=42;

cmpthese (-5,
   {
       'squote' => sub { my $v='Die $zahl 42 ist ein $$ TEST $$'; },
       'squote-dot' => sub { my $v='Die $zahl '.$zahl.' ist ein $$ '.$var.' $$'; },
       'dquote' => sub { my $v="Die \$zahl $zahl ist ein \$\$ $var \$\$"; },
       'dquote-dot' => sub { my $v="Die \$zahl ".$zahl." ist ein \$\$ ".$var." \$\$"; }
   }
);
Dabei kam raus:

Code: Alles auswählen

                Rate  duote-dot squote-dot     dquote     squote
duote-dot   901633/s         --        -9%       -12%       -76%
squote-dot  987765/s        10%         --        -4%       -74%
dquote     1028860/s        14%         4%         --       -73%
squote     3762700/s       317%       281%       266%         --

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 04.01.2007 07:54:25

sentolacco hat geschrieben:ausserdem bin ich auf der suche nach dem "Input Record Separator" nicht wirklich fündig geworden...
Diese Variablen sind in der "perlvar" Dokumentation beschrieben:

Code: Alles auswählen

gms@gms4:~$ perldoc perlvar
Das Setzen dieses Separators hat den Vorteil, daß du damit auch etwas kompliziertere ( mehrzeilige ) html-Tags lesen kannst, ohne das ganze Dokument im Speicher halten zu müssen.

Code: Alles auswählen

gms@gms4:~$ cat x.html
 <input
name="var1" type="text" >
 <input name="var2" type="text"
 >
 <input name="var3" type="text">
gms@gms4:~$ 
gms@gms4:~$ perl -e '$/=">"; while(<>) { $l=$1 if /(<input.*>)/is; $l=~ s/\n//g; print $l,"\n"; }' x.html
<input name="var1" type="text" >
<input name="var2" type="text" >
<input name="var3" type="text">

Spätestens dann, wenn du verschachtelte html-Tags parsen möchtest, solltest du aber wirklich "nepos" Rat beherzigen und spezielle Parser-Module verwenden

Gruß
gms

Benutzeravatar
Sid Burn
Beiträge: 47
Registriert: 16.11.2006 15:18:02
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: /universe/earth/europe/germany/nrw/essen
Kontaktdaten:

Beitrag von Sid Burn » 04.01.2007 22:14:22

@topeg
Dein Test testet aber nicht ganz das gleiche. Ich denke du wolltest einfach nur testen wie ein einzelnes Hochkommata gegenüber double quotes abschneidet, und das gleiche nochmals wenn man den String mit dots zusammenfügt.

Allerdings wenn du mal genau drauf achtest entsteht zwar am ende immer der selbe String, allerdings hast du bei squote 0 Variableninterpolationen. Bei dquote kommt 1 vor, und bei den anderen beiden 2. Die Performance unterschiede dürften wohl hauptsächlich durch die unterschiedliche Anzahl von Variableninterpolationen zustande kommen, und nicht durch die Benutzung von single/double quote oder den dots.

Ich hab dein Beispiel mal etwas abgeändert so das in allen Beispielen keine Variableninterpolation vor kommt. So kann man vergleichen wie schnell dquote gegenüber squote ist. Und dann nochmal wenn man es mit dots zusammenfügt. Allerdings kann ich aus meinen Benchmarks keine Schlüsse ziehen, und Sie auch nicht logisch erklären. Auch mehrere benchmarks hintereinander geben immer anscheind ein "willkürliches" ergebnis.

quote.pl

Code: Alles auswählen

#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw/cmpthese/;

cmpthese (-5,
   {
       'squote' => sub { my $v='Die $zahl 42 ist ein $$ TEST $$'; },
       'dquote' => sub { my $v="Die \$zahl 42 ist ein \$\$ TEST \$\$"; },
       'squote-dot' => sub { my $v='Die $zahl '.'42'.' ist ein $$ '.'TEST'.' $$'; },
       'dquote-dot' => sub { my $v="Die \$zahl "."42"." ist ein \$\$ "."TEST"." \$\$"; },
   }
);

Code: Alles auswählen

$ for i in 0 1 2 3 4; do ./quote.pl ; done
                Rate dquote-dot     dquote     squote squote-dot
dquote-dot 4288545/s         --        -2%        -4%        -5%
dquote     4393738/s         2%         --        -2%        -2%
squote     4483787/s         5%         2%         --        -0%
squote-dot 4491115/s         5%         2%         0%         --

                Rate     dquote squote-dot     squote dquote-dot
dquote     3909235/s         --        -2%       -12%       -23%
squote-dot 3982222/s         2%         --       -10%       -22%
squote     4418279/s        13%        11%         --       -13%
dquote-dot 5091228/s        30%        28%        15%         --

                Rate     dquote dquote-dot squote-dot     squote
dquote     4047130/s         --        -4%       -21%       -24%
dquote-dot 4233094/s         5%         --       -18%       -20%
squote-dot 5135157/s        27%        21%         --        -3%
squote     5314750/s        31%        26%         3%         --

                Rate     dquote     squote squote-dot dquote-dot
dquote     4047266/s         --        -6%       -21%       -21%
squote     4292215/s         6%         --       -17%       -17%
squote-dot 5147390/s        27%        20%         --        -0%
dquote-dot 5154487/s        27%        20%         0%         --

                Rate     dquote squote-dot     squote dquote-dot
dquote     4128011/s         --        -7%       -11%       -23%
squote-dot 4419844/s         7%         --        -5%       -18%
squote     4632477/s        12%         5%         --       -14%
dquote-dot 5380122/s        30%        22%        16%         --
P.S.:
Bist du der "topeg" von perl-community.de?

ToPeG
Beiträge: 437
Registriert: 14.04.2004 00:42:06

Beitrag von ToPeG » 07.01.2007 03:30:16

Mir ging es gerade um die Variableninterpolation.

Dein Code hat aber auch ein Problem. Perl ist so "intelligent", das es die Strings schon beim wandeln in den Bitecode zusammenfügt und später nurnoch verschiebt. Die von dir gemessenen Differenzen kommen daher vom Betriebssystem und sind deshalb auch so willkührlich. Du hättest "eval" verwenden müssen.

Der String mit dem Singelpuote ohne Dots ist sozusagen die Referenz. Alle anderen müssen langsamer sein.
Innerhalb eines Strings mit Doublequote werden Werte etwas anders eingefügt als durch ein zusammenfügen mittels Punkt. Ich wollte schauen wie schnell die jeweilige vorgehensweise ist.

Code: Alles auswählen

                Rate  duote-dot squote-dot     dquote     squote
duote-dot   912796/s         --        -8%       -10%       -77%
squote-dot  992798/s         9%         --        -3%       -75%
dquote     1019883/s        12%         3%         --       -75%
squote     4016820/s       340%       305%       294%         --

                Rate  duote-dot     dquote squote-dot     squote
duote-dot   904002/s         --       -16%       -18%       -77%
dquote     1070079/s        18%         --        -3%       -73%
squote-dot 1105117/s        22%         3%         --       -72%
squote     3962043/s       338%       270%       259%         --

                Rate  duote-dot     dquote squote-dot     squote
duote-dot   905747/s         --        -8%        -9%       -76%
dquote      984197/s         9%         --        -2%       -74%
squote-dot 1000784/s        10%         2%         --       -74%
squote     3807142/s       320%       287%       280%         --

                Rate  duote-dot squote-dot     dquote     squote
duote-dot   911957/s         --        -8%        -9%       -75%
squote-dot  990587/s         9%         --        -1%       -73%
dquote     1004122/s        10%         1%         --       -73%
squote     3690088/s       305%       273%       267%         --

                Rate  duote-dot squote-dot     dquote     squote
duote-dot   890279/s         --       -14%       -16%       -79%
squote-dot 1035001/s        16%         --        -3%       -75%
dquote     1064253/s        20%         3%         --       -75%
squote     4220471/s       374%       308%       297%         --
Es scheit egal zu sein, ob man Mit Singelquotes und Punkten arbeitet oder nut mit Doublequotes. Beide sind gleich schnell. Das Doublequotes und Punkte zusammen langsamer sind, kann damit erklären, daß jeder Teilstring und die Variablen getrtennt abgearbeitet wird.

Deine Werte sagen nur, daß es egal ist wie man einen String zusammenfügt. Die Differenzen entsehen erst bei der Interpretation der Variablen. Es scheint dabei keinen Unterschied zu machen ob die Variaben im String eingebettet sind oder nicht. Nur Doublequotes und Variablen außerhalb des Stringes durch Punkte hinzugefügt, sollte man vermeiden. Übrigens auch "sprintf" ist recht langsam.


Zu deinem p.s.:
Ich bin eben dieser, und wahrscheinlich eben jener Sid Burn. :-)

Benutzeravatar
Sid Burn
Beiträge: 47
Registriert: 16.11.2006 15:18:02
Lizenz eigener Beiträge: MIT Lizenz
Wohnort: /universe/earth/europe/germany/nrw/essen
Kontaktdaten:

Beitrag von Sid Burn » 07.01.2007 22:31:41

Zu deinem p.s.:
Ich bin eben dieser, und wahrscheinlich eben jener Sid Burn. Smile
Ganz genau. ;)


Ansonsten ist mir dein Test trotzdem noch ein Rätsel. Okay squote stellt die Referenz da und alles andere muss langsamer sein. Aber du sagst ja das du die unterschiedlichen Vorgehensweisen Testen möchtest ob es schneller ist eine Variable innerhalb eines Strings zu interpolieren, oder ob es schneller ist wenn man sie mit dot an einen String anhängt.

Da aber dquote trotzdem nur 1 Variablen interpolation enthält gegenüber den anderen beiden verstehe ich nicht wie du genau einen Vergleich aufbaust mit unterschiedlichen Ausgangspunkten.


Aber mir mag das jetzt erstmal egal sein. Ich baue mir die Strings so zusammen wie sie am besten lesbar sind. ;)

ToPeG
Beiträge: 437
Registriert: 14.04.2004 00:42:06

Beitrag von ToPeG » 08.01.2007 20:44:32

Aber "dquote" enthält doch sowohl "$zahl" als auch "$var" wie alle anderen Varianten auch (von "squote" mal abgesehen).
Damit haben alle diese Varianten die selben Eigenschaften.

Sicher es bleibt immer dem Programmierer überlassen, was er verwenden möchte. :-)

Antworten