[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: kleines RegEx Problem



On Wed, May 24, 2006 at 03:06:41AM +0200, Andreas Pakulat wrote:
> Ich moechte nun mit einer Regex darauf matchen, folgende regex macht
> fast was sie soll:
> 
> '^([^_]+)_([^_]+)(_.+)*(\.orig\.tar\.gz|\.tar\.gz|\.diff\.gz|\.dsc|\.deb|\.udeb|\.package|\.source)$'

Zum einen kannst Du den "." in der letzten Klammer ausfaktorisieren. Das
loest Dein Problem nicht, macht die Regexp aber kuerzer ;-)

'^([^_]+)_([^_]+)(_.+)*(\.(orig\.tar\.gz|tar\.gz|diff\.gz|dsc|deb|udeb|package|source))$'

> Das Problem dabei: Bei foobar_version.orig.tar.gz erhalte ich fuer
> 
> \1 = foobar
> \2 = version.orig
> \3 = tar.gz

Der Grund ist, dass die Operatoren * und + gierig (greedy) sind und Deine
zweite Klammer somit den ganzen Rest schluckt. Regexps werden in der Regel
von links her abgearbeitet. Damit der Matcher dann Dein Pattern erfuellen
kann, muss er jetzt noch versuchen, die letzte Klammer zu erfuellen. Dafuer
kann er nur den gierigen Operatoren Stueck fuer Stueck weniger geben. Das
geschieht dann de facto "von rechts" und da ist Dein Problem: tar.gz ist
halt kuerzer als orig.tar.gz und trifft damit eher zu.

Wenn Du das mit obigen Mitteln loesen willst, dann musst Du im Prinzip die
Regexp verdoppeln und in der rechten Klammer des linken Teilausdrucks Deine
praeferierten Pattern angeben. Nur sind das im Prinzip zwei Regexps in eine
gefaltet und ausserdem klappt's bei den meisten Matchern weiterhin nicht,
weil die per Definition den "leftmost longest match" finden... da Du alles
haben willst, ist "leftmost" hier egal, aber "longest" gilt fuer die
Teilausdrueck immer noch.

> Jemand nen Tipp wie ich das mit einer regex erschlagen kann (mit
> mehreren ist es kein Problem, das stoert dann aber im Progamm etwas)?

Du hast ja nicht gesagt, in welcher Sprache fuer welchen Matcher. ;-) Bei
mir klappt es mit dem beigefügten Perl-Programm. Das erzeugt folgenden
Output:

nexus[1615]: perl regexp.pl 
foobar_version_arch.deb
\1=foobar
\2=version
\3=_arch
\4=.deb
foobar_version.orig.tar.gz
\1=foobar
\2=version
\3=
\4=.orig.tar.gz
foobar_version.tar.gz
\1=foobar
\2=version
\3=
\4=.tar.gz
foobar_version.orig.tar.gz
\1=foobar
\2=version
\3=
\4=.orig.tar.gz

Ein paar Erlaeuterungen zum Programm:

 - Perl hat seit Perl5 neben den Operatoren * und + auch noch *? und +? die
beide nicht gierig sind, also so wenig wie moeglich matchen. Damit loest
sich das Problem in (fast) nichts auf.

 - Der Match findet in dem Ausdruck /pattern/ statt, dank des x-Flags (also
/pattern/x) kann der Ausdruck auch ueber mehrere Zeilen geschrieben werden.
Leerzeichen werden in dem Fall einfach ignoriert. Die Regexp wird daurch
uebersichtlicher.

 - Der DATA-Filehandle liest alles, was nach __END__ im Script steht,
deshalb brauche ich Dir nicht zwei Files zu attachen.

	--jc

P.S. Die eigenwillige Operatoren-Syntax kommt daher, dass beim Wechsel von
Perl4 nach Perl5 alte Programme weiterhin unveraendert laufen sollten,
deshalb hat Larry Wall eine frueher ungueltige Syntax als Erweiterung
gewaehlt. Betrachte es einfach als Idiom (aka nuetzlichen Slang).

-- 
  Ignorance more frequently begets confidence than does knowledge.
	-- Charles Darwin
#! /usr/bin/perl

while (<DATA>) {
  print $_;
  /^([^_]+)_([^_]+?)(_.+)*
   (\.(orig\.tar\.gz|tar\.gz|diff\.gz|dsc|deb|udeb|package|source))$/x;

  print "\\1=$1\n";
  print "\\2=$2\n";
  print "\\3=$3\n";
  print "\\4=$4\n";
}

__END__
foobar_version_arch.deb
foobar_version.orig.tar.gz
foobar_version.tar.gz
foobar_version.orig.tar.gz

Reply to: