Dies ist die official FAQ answer minus alle nachfolgenden Bearbeitungen.
Ihr erster Versuch sollte wahrscheinlich das Modul Text::Balanced sein, das seit Perl 5.8 in der Perl-Standardbibliothek ist. Es hat eine Vielzahl von Funktionen, um mit kniffligen Texten umzugehen. Das Modul Regexp::Common unterstützt Sie auch bei der Bereitstellung von vordefinierten Mustern, die Sie verwenden können.
Ab Perl 5.10 können Sie ausgeglichenen Text mit regulären Ausdrücken unter Verwendung rekursiver Muster abgleichen. Vor Perl 5.10 mussten Sie auf verschiedene Tricks zurückgreifen, z. B. Perl-Code in (??{})
Sequenzen verwenden.
Hier ist ein Beispiel mit einem rekursiven regulären Ausdruck. Das Ziel besteht darin, den gesamten Text in spitzen Klammern zu erfassen, einschließlich des Textes in verschachtelten spitzen Klammern. Dieser Beispieltext hat zwei "Hauptgruppen": eine Gruppe mit einer Verschachtelungsebene und eine Gruppe mit zwei Verschachtelungsebenen. Es gibt insgesamt fünf Gruppen in spitzen Klammern:
I have some <brackets in <nested brackets> > and
<another group <nested once <nested twice> > >
and that's it.
Der reguläre Ausdruck den ausgewogenen Text verwendet zwei neue anzupassen (zu Perl 5.10) für reguläre Ausdrücke Funktionen. Diese sind in perlre behandelt und dieses Beispiel ist eine modifizierte Version von einem in dieser Dokumentation.
Erstens, das Hinzufügen des neuen Possessiv +
zu einem beliebigen Quantifizierer findet die längste Übereinstimmung und führt nicht zurück. Das ist wichtig, da Sie alle Winkel durch die Rekursion und nicht durch Rückverfolgung behandeln wollen. Die Gruppe [^<>]++
findet eine oder mehrere nicht-spitze Klammern ohne Rückverfolgung.
Zweitens bezieht sich das neue (?PARNO)
auf das Untermuster in der bestimmten Erfassungsgruppe, die durch PARNO
gegeben wird. In der folgenden Regex findet (und merkt) sich die erste Erfassungsgruppe den ausgeglichenen Text und Sie benötigen dasselbe Muster innerhalb des ersten Puffers, um über den verschachtelten Text zu gelangen. Das ist der rekursive Teil. Die (?1)
verwendet das Muster in der äußeren Erfassungsgruppe als einen unabhängigen Teil der Regex.
setzen sie alle zusammen, Sie haben:
#!/usr/local/bin/perl5.10.0
my $string =<<"HERE";
I have some <brackets in <nested brackets> > and
<another group <nested once <nested twice> > >
and that's it.
HERE
my @groups = $string =~ m/
( # start of capture group 1
< # match an opening angle bracket
(?:
[^<>]++ # one or more non angle brackets, non backtracking
|
(?1) # found <or>, so recurse to capture group 1
)*
> # match a closing angle bracket
) # end of capture group 1
/xg;
$" = "\n\t";
print "Found:\n\[email protected]\n";
Die Ausgabe zeigt, dass Perl die beiden großen Gruppen gefunden:
Found:
<brackets in <nested brackets> >
<another group <nested once <nested twice> > >
Mit ein wenig zusätzliche Arbeit, können Sie die alle das bekommen Gruppen in spitzen Klammern, auch wenn sie in anderen spitzen Klammern sind. Jedes Mal, wenn Sie eine ausgeglichene Übereinstimmung erhalten, entfernen Sie deren äußeres Begrenzungszeichen (das ist dasjenige, das Sie gerade gefunden haben, damit es nicht wieder übereinstimmt) und fügen es einer Warteschlange mit zu verarbeitenden Zeichenfolgen hinzu. Machen Sie das solange, bis Sie keine Übereinstimmungen mehr erhalten:
#!/usr/local/bin/perl5.10.0
my @queue =<<"HERE";
I have some <brackets in <nested brackets> > and
<another group <nested once <nested twice> > >
and that's it.
HERE
my $regex = qr/
( # start of bracket 1
< # match an opening angle bracket
(?:
[^<>]++ # one or more non angle brackets, non backtracking
|
(?1) # recurse to bracket 1
)*
> # match a closing angle bracket
) # end of bracket 1
/x;
$" = "\n\t";
while(@queue)
{
my $string = shift @queue;
my @groups = $string =~ m/$regex/g;
print "Found:\n\[email protected]\n\n" if @groups;
unshift @queue, map { s/^<//; s/>$//; $_ } @groups;
}
Der Ausgang zeigt alle Gruppen an. Die äußersten Übereinstimmungen werden zuerst angezeigt und die verschachtelten Übereinstimmungen werden später angezeigt:
Found:
<brackets in <nested brackets> >
<another group <nested once <nested twice> > >
Found:
<nested brackets>
Found:
<nested once <nested twice> >
Found:
<nested twice>