Das Problem: Ich codiere eine Bibliothek, die vom Benutzer bereitgestellten regulären Ausdruck erhält, der eine unbekannte Anzahl von Erfassungsgruppen enthält, die für andere Eingaben ausgeführt werden sollen, und möchte den Wert aller in einer Zeichenfolge verketteten Erfassungsgruppen extrahieren (zur weiteren Verarbeitung an anderer Stelle).
Es ist trivial, wenn die Anzahl der Erfassungsgruppen im Voraus bekannt ist, da ich sie nur spezifiziere:
#!/usr/bin/perl -w
my $input = `seq -s" " 100 200`;
my $user_regex =
qr/100(.*)103(.*)107(.*)109(.*)111(.*)113(.*)116(.*)120(.*)133(.*)140(.*)145/;
if ($input =~ $user_regex) { print "$1 $2 $3 $4 $5 $6 $7 $8 $9 $10\n"; }
Korrekt erzeugt (ignorieren Sie das zusätzliche Leerzeichen):
101 102 104 105 106 108 110 112 114 115 117 118 119
121 122 123 124 125 126 127 128 129 130 131 132
134 135 136 137 138 139 141 142 143 144
Wenn es jedoch mehr als 10 Erfassungsgruppen gibt, gehen Daten verloren, wenn ich den Code nicht ändere. Da die Anzahl der Erfassungsgruppen unbekannt ist, gehe ich derzeit mit Hunderten von manuell festgelegten Übereinstimmungen ("$ 1" bis "$ 200") unter no warnings
Pragma und hoffe, dass es ausreicht, aber es scheint nicht besonders sauber zu sein oder robust.
Im Idealfall möchte ich etwas, das wie values %+
für benannte Erfassungsgruppen funktioniert, jedoch für nicht benannte Erfassungsgruppen. Ist es in Perl 5.24 möglich? Oder welchen weniger klobigen Ansatz würden Sie zum Abrufen von Inhalten aller nummerierten Erfassungsgruppen empfehlen?
5 Antworten
Vielleicht können Sie einfach in ein Array erfassen?
my @captured = $input =~ $user_regexp;
if( @captured ) { print join " ", @captured; print "\n"; }
Wenn Sie die nummerierten Erfassungsvariablen unbedingt verwenden müssen, verwenden Sie eval:
my $input = "abc";
my $re = qr/(.)(.)(.)/;
if( $input =~ $re){
my $num = 1;
print "captured \$$num = ". eval("\$$num") ."\n" and $num++
while eval "defined \$$num";
}
Oder nur:
my $input = "abc";
my $re = qr/(.)(.)(.)/;
if( $input =~ $re){
my $num = 1;
print "captured \$$num = $$num\n" and $num++ while defined $$num;
}
... aber dieses letzte Beispiel mit skalaren Referenzen funktioniert nicht unter use strict
.
Wenn Sie Perl v5.26.2 (derzeit die neueste Version) oder höher ausführen, können Sie das integrierte Array @{^CAPTURE}
verwenden, anstatt selbst auf die Erfassungsvariablen zuzugreifen
Wie bei einem normalen Array beträgt die Anzahl der Captures scalar @{^CAPTURE}
und die Indizes reichen von Null bis $#{^CAPTURE}
Beachten Sie, dass das Array mit der letzten erfolgreichen Musterübereinstimmung gefüllt ist. Daher sollten Sie genau wie die Erfassungsvariablen selbst den Status einer Musterübereinstimmung überprüfen, bevor Sie den Inhalt von @{^CAPTURE}
verwenden.
Für Version 5.24 gibt es kein Array aller erfassten Werte, aber Sie können sie mithilfe der Start- / Endposition jeder Übereinstimmung extrahieren:
my $s = <some string>;
my $re = <some regex with captures>;
my @matches;
if ($s =~ $re) {
for my $i (0 .. $#-) {
push @matches, substr($s, $-[$i], $+[$i] - $-[$i]);
}
}
Die von Michael Carman und Borodin werden in Perlvar hilfreich dokumentiert - http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions.
Trotzdem habe ich Ideen aus mehreren Beiträgen zu einer meiner Meinung nach umfassenderen Antwort zusammengefasst:
#!/usr/bin/env perl
use Modern::Perl;
my @a = 'abcde' =~ /(.).(.).(.)/;
say do { # map probably creates a temp anonymous array of capture strings
no strict 'refs';
join ' ', map { "$$_" } 1..$#-
};
say do { # no copy to array but eval
eval '"' . join(" ", map { "\$$_" } 1..$#-) . '"';
};
say "@a"; # still not clear from OP why this wasn't the answer
Sie können die Zahlen in $ 1 $ 2 usw. als Variablen behandeln
$t="abcdefghijklmnop";
$t=~/(.)(.)(.)(.)(.)(.)(.)/;
print $$_ for 1..10;
Sie können streng umgehen,
use strict;
$t="abcdefghijklmnop";
$t=~/(.)(.)(.)(.)(.)(.)(.)/;
{
no strict;
print $$_ for 1..10;
}
Sie können sie auch in ein Array einfügen (entnommen aus http://perldoc.perl.org/perlre). html)
use strict;
my $t="abcdefghijklmnop";
my @a=$t=~/(.)(.)(.)(.)(.)(.)(.)/;
print "@a";
Obwohl beide nicht perfekt sind, bedeutet die Verwendung strenger Referenzen, dass Sie die Namen Ihrer Variablen kennen. Daher kennen Sie im Idealfall Ihre Variablennamen, z. B. wie viele Erfassungsgruppen Sie verwendet haben
Verwandte Fragen
Neue Fragen
regex
Reguläre Ausdrücke bieten eine deklarative Sprache, um Muster innerhalb von Zeichenfolgen abzugleichen. Sie werden häufig zur Validierung, Analyse und Transformation von Zeichenfolgen verwendet. Da reguläre Ausdrücke nicht vollständig standardisiert sind, sollten alle Fragen mit diesem Tag auch ein Tag enthalten, das die jeweilige Programmiersprache oder das jeweilige Tool angibt.