[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