[Bielefeld-pm] Relative Regex-Backrefs
Nils Diewald
nils at diewald-online.de
Mi Jul 29 02:57:25 PDT 2009
Hallo!
Das Problem war: Wie kann ich Matching-Rückwärtsreferenzen
relativ statt absolut angeben, um mehrfaches Kompilieren
eines Ausdrucks zu vermeiden. Und Jürgen hat schon die
Antwort für Perl 5.10 geliefert: Mit \g{-n}. Danke dafür!
Ich hatte mir die 5.10-Spezialitäten wohl noch nicht
ausreichend genug angesehen.
Christians Beispiel muss leicht umgeändert werden,
um das Problem besser zu veranschaulichen:
[code]
#!perl
use warnings;
use strict;
# Ein wenig komplizierteres Attribut-Matching, damit sich das
# Vorkompilieren lohnt. ;) Keine Ahnung, ob's einfacher geht.
# Soll auch 'warn:(\'c:\\test\\\')' oder 'warn:("hallo")' greifen.
my $re_param = qr/
(\w+)
\s*=\s*
(['"])
(
(?:
(?:
(?:(?<!\\)(?:\\))
(?:\\\\)*
)?
[^\2]
)+?
)
\2/x;
# Die Backreference ist auf 2 eingestellt, für >(['"])<
my $s;
{
local $/ = undef;
$s = <DATA>;
};
# Gesucht werden die öffnenden Tags von ELEMENT oder A
# Hier wird der selbe Ausdruck wie oben verwandt, aber mit dem
# absoluten Wert 4. So lässt er sich nicht einmal vorkompilieren
# als Teil-Regex, da Perl die benummerte Gruppe benötigt.
while ($s =~ /
<
(ELEMENT|A)
(
(?:
\s+
(\w+)
\s*=\s*
(['"])
(
(?:
(?:
(?:(?<!\\)(?:\\))
(?:\\\\)*
)?
[^\4]
)+?
)
\4
)+
)?
\s*>/gxo) {
print $1;
if ($2) {
my $t = $2;
# Hier der vorkompilierte Ausdruck
while ($t =~ m/\s$re_param/go) {
print ' ',$1,' = (',$3,');';
};
};
print "\n";
};
__DATA__
erste zeile <ELEMENT var1='wert:(\'Hallo\')' var2="wert2">Inhalt</ELEMENT> mitte <A var3='wert3'><i>bla</i></A> Rest
zweite zeile <ELEMENT var1='wert1' var2="wert2">Inhalt <i>irgendwelcher</i> Art</ELEMENT> Rest
dritte zeile <ELEMENT var1='wert1'>Inhalt irgendwelcher Art</ELEMENT> Rest
vierte zeile <ELEMENT var1=wert1>Inhalt irgendwelcher Art</ELEMENT> Rest
fuenfte zeile <ELEMENT>Inhalt irgendwelcher Art</ELEMENT> Rest
[/code]
Ausgabe:
ELEMENT var1 = (wert:(\'Hallo\')); var2 = (wert2);
A var3 = (wert3);
ELEMENT var1 = (wert1); var2 = (wert2);
ELEMENT var1 = (wert1);
ELEMENT
Da wäre die Wartung und Vorkompilierung eines Ausdrucks wünschenswert, dachte ich mir ...
Das ganze in Perl 5.10 müsste also so gehen:
[code]
my $re_param = qr/
(\w+)
\s*=\s*
(['"])
(
(?:
(?:
(?:(?<!\\)(?:\\))
(?:\\\\)*
)?
[^\g{-2}]
)+?
)
\g{-2}/x;
# (...)
while ($s =~ /
<
(ELEMENT|A)
(
(?:
\s+
$re_param
)+
)?
\s*>/gxo) {
# (...)
[/code]
Werde es ausprobieren, sobald ich 5.10 installiert habe! Danke Euch allen!
Beste Grüße,
Nils
taulmarill at xgn.de schrieb:
> Mahlzeit,
>
> und ich hatte doch recht :-)
>
> Mit Perl 5.10 gibt es die Möglichkeit mit \g{n} einen Buffer anzusprechen,
> wenn n positiv ist, funktioniert das genau so wie \n. Also \g{1} ist
> gleich \1. Wenn n aber negativ ist, läuft das relativ zur aktuellen
> Position:
>
> /
> (Y) # buffer 1
> ( # buffer 2
> (X) # buffer 3
> \g{-1} # backref to buffer 3
> \g{-3} # backref to buffer 1
> )
> /x
>
> Das müsste doch eigentlich genau das tun, was Nils wollte. Außerdem gibt
> es auch die Möglichkeit capture buffers zu benennen. Schaut euch einfach
> mal hier http://perldoc.perl.org/perlre.html den Abschnitt "Capture
> buffers" an.
>
> Wie gesagt, das ganze funktioniert erst mit Perl >= 5.10, aber so was ist
> ja schnell kompiliert.
>
>
> Gruß,
> Jürgen
>
> Am Mi, 29.07.2009, 09:41, schrieb c.duehl at gmx.de:
>
>> Guten Morgen,
>>
>> ich hab versucht, Nils RE-Problem irgendwie nachzubauen, um eine Lösung zu
>> basteln.
>>
>> Neben der mit den Doppelten REs (fangend / nicht fangend) von gestern,
>> gäbe es noch die Möglichkeit, sich Stückchenweise vorzuarbeiten, oder aber
>> den bisherigen Ausdruck zu nutzen und einen zweiten Test nachzulagern.
>>
>> So nach dem Motto
>>
>> if (my @treffer = m~schwierige RE~) {
>> my $n = @treffer;
>> if ($treffer[$n-1] eq $treffer[$n-3]) {
>> # eigentliche Behandlung hier
>> }
>> }
>>
>>
>>
>> Ich komme allerdings gar nicht so weit. Vermutlich ist mein Beispiel nicht
>> komplex genug. Ich poste es trotzdem mal:
>>
>>
>> [code]
>> #!/usr/bin/perl
>> use strict;
>> use warnings;
>>
>> my $re_param =
>> qr~[\w]+
>> \s*
>> =
>> \s*
>> ['"]?
>> [^'"<>]+
>> ['"]?
>> \s*
>> ~x;
>>
>> while (<DATA>) {
>> chomp;
>> print "zeile $.: $_\n";
>> my @werte =
>> m~<
>> (ELEMENT|A)
>> (?:
>> \s+
>> ((?:$re_param\s*)+)
>> )?
>> >
>> (
>> (?:
>> (?!</\1>).
>> )*
>> )
>> </\1>
>> ~gx;
>> for my $w (@werte) {
>> print " ", (defined $w ? $w : '---'), "\n";
>> }
>> }
>>
>>
>> __DATA__
>> erste zeile <ELEMENT var1='wert1' var2="wert2">Inhalt</ELEMENT> mitte <A
>> var3='wert3'><i>bla</i></A> Rest
>> zweite zeile <ELEMENT var1='wert1' var2="wert2">Inhalt
>> <i>irgendwelcher</i> Art</ELEMENT> Rest
>> dritte zeile <ELEMENT var1='wert1'>Inhalt irgendwelcher Art</ELEMENT> Rest
>> vierte zeile <ELEMENT var1=wert1>Inhalt irgendwelcher Art</ELEMENT> Rest
>> fuenfte zeile <ELEMENT>Inhalt irgendwelcher Art</ELEMENT> Rest
>> [/code]
>>
>> Ergebnis:
>>
>> [code]
>> zeile 1: erste zeile <ELEMENT var1='wert1' var2="wert2">Inhalt</ELEMENT>
>> mitte <A var3='wert3'><i>bla</i></A> Rest
>> ELEMENT
>> var1='wert1' var2="wert2"
>> Inhalt
>> A
>> var3='wert3'
>> <i>bla</i>
>> zeile 2: zweite zeile <ELEMENT var1='wert1' var2="wert2">Inhalt
>> <i>irgendwelcher</i> Art</ELEMENT> Rest
>> ELEMENT
>> var1='wert1' var2="wert2"
>> Inhalt <i>irgendwelcher</i> Art
>> zeile 3: dritte zeile <ELEMENT var1='wert1'>Inhalt irgendwelcher
>> Art</ELEMENT> Rest
>> ELEMENT
>> var1='wert1'
>> Inhalt irgendwelcher Art
>> zeile 4: vierte zeile <ELEMENT var1=wert1>Inhalt irgendwelcher
>> Art</ELEMENT> Rest
>> ELEMENT
>> var1=wert1
>> Inhalt irgendwelcher Art
>> zeile 5: fuenfte zeile <ELEMENT>Inhalt irgendwelcher Art</ELEMENT> Rest
>> ELEMENT
>> ---
>> Inhalt irgendwelcher Art
>> [/code]
>>
>> Gruß Christian
>> _______________________________________________
>> Bielefeld-pm mailing list
>> Bielefeld-pm at pm.org
>> http://mail.pm.org/mailman/listinfo/bielefeld-pm
>>
>>
>
>
> _______________________________________________
> Bielefeld-pm mailing list
> Bielefeld-pm at pm.org
> http://mail.pm.org/mailman/listinfo/bielefeld-pm
>
Mehr Informationen über die Mailingliste Bielefeld-pm