2009-02-25 15 views
-2

Ich versuche, die Zeichenfolge $searchCriteria in der If-Bedingung zu erweitern. Irgendwelche Hinweise?Wie kann ich Variablen in einer Perl-Zeichenfolge erweitern?

use strict; 

my $argNum; 
my $searchCriteria = ""; 
foreach $argNum (0 .. $#ARGV) { 
    $searchCriteria = $searchCriteria . "(\$_ =~ \/" . $ARGV[$argNum] . "\/i) && "; 
} 
$searchCriteria =~ s/&& $//; 
#print $searchCriteria; 

open IP, "<vm.txt" or die $!; 

my @fileContents = <IP>; 
foreach (@fileContents) { 
    if (${$searchCriteria}) { 
     print $_; 
    } 
} 
+0

Was versuchen Sie zu erreichen? Was ist der Sinn dieses Codes? –

+0

@brian: Es sieht so aus, als wolle er es auswerten. – Axeman

+0

Ja, aber warum? Zu fragen, wie man etwas macht, ist oft die falsche Frage. Ich möchte wissen, was er zu erreichen versucht. –

Antwort

1

Es sieht aus wie Sie eval wollen, ob die Linie alle Argumente in @ARGV enthält.

Ich denke, dies könnte mehr nach dem, was Sie suchen, und schmackhafter für die Perl-Crowd als gut sein.

use FileHandle; 
use List::MoreUtil qw<all>; 

my @regexes = map { qr/\L$_\E/i } @ARGV; 

my $fh = FileHandle->new('<vm.txt'); 
die "Err: $!" unless $fh; 

foreach my $line (<$fh>) { 
    print $line if all { $line =~ m/$_/i } @regexes; 
} 

Natürlich, wenn Sie etwas Kompression, weil Sie gehen führen Sie den Test immer und immer wieder in einer einzigen Ausführung wollten, könnten Sie eine Unter dafür erstellen.

sub create_compound_test { 
    my $test_sub_text = "sub (_) {\n local \$_ = shift;"; 

    foreach my $arg (map { lc; } @_) { 
     $test_sub_text .= "\n return unless m/$arg/i;"; 
    } 
    $test_sub_text .= "\n return 1;\n}"; 

    my $test_sub = eval $test_sub_text; 
    Carp::croak "Could not create test:\n $test_sub_text - [email protected]" if [email protected]; 
    return $test_sub; 
} 

Es ist das gleiche wie $_ =~ /blah/i && $_ =~ /blah2/ ...

Dann wäre es:

my $match_all = create_compound_test(@ARGV); 
foreach (<$fh>) { 
    print if $match_all->(); 
} 

Aber ich würde wahrscheinlich eher dies tun:

my $match_all = create_compound_test(@ARGV); 
foreach (grep { $match_all->(); } <$fh>) { 
    print; 
} 

... oder sogar ...

print foreach grep { $match_all->(); } <$fh>; 
1

Uhm ... Ich bin mir nicht sicher, was die Frage ist ... aber irgendwie habe ich den Verdacht, dass Sie den qr // Ausdruck (zitiert reguläre Ausdrücke) benötigen.

0

Was ist Ihre Frage? Vielleicht suchen Sie nach ack? Es scheint, als ob Sie versuchen, eval eine Zeichenfolge (in der Regel eine schlechte Idee). Sie sollten mindestens qr // verwenden. Vielleicht etwas wie:

my @regexs = map qr/(?i)$_/, @ARGV; 
my $search = sub { 
    for my $re (@regexes) { 
    return unless /$re/; 
    } 
    return 1; 
} 
open IP, "<", "vm.txt" or die "Err: $!"; 
while (<IP>) { 
    print $_ if $search->(); 
} 

Dies könnte leicht verbessert werden. Und es ist nicht getestet. YMMV.

3

Wenn Ihre Suchkriterien reguläre Ausdrücke sind, sollten Sie Ihre eigene kompilierte Regexp vorbereiten. Beachten Sie auch die Verwendung der while-Schleife (beim Lesen der Datei), um übermäßigen Speicherverbrauch zu vermeiden. Wenn Sie möchten, Zeilen ein Argument enthalten:

use strict; 
use warnings; 

my $searchRe = do { 
    my $searchCriteria = join '|', map "(?:$_)", @ARGV; 
    qr/$searchCriteria/i; 
}; 

open my $fh, '<', 'vm.txt' or die $!; 

while (<$fh>) { 
    print if m/$searchRe/; 
} 

close $fh; 

oder wenn Sie wollen Linien alle Einsen enthalten:

use strict; 
use warnings; 

my $searcher = do { 
    my @searchCriteria = map qr/$_/i, @ARGV; 
    sub { 
    # study; # study can help for long lines or lot of regular expressions 
    for my $re (@searchCriteria) { 
     return unless m/$re/; 
    } 
    return 1 
    } 
}; 

open my $fh, '<', 'vm.txt' or die $!; 

while (<$fh>) { 
    print if $searcher->(); 
} 

close $fh; 

(Beachten Sie, dass Sie ein \Q und \E um die $_ wenn die Befehlszeile wollen könnten Argumente sind Zeichenfolgen anstelle von regulären Ausdrücken.)

Schließlich, wenn Sie die Geschwindigkeit für viele Suchkriterien verbessern möchten, verwenden Sie Regexp::Optimizer.

use Regexp::Optimizer; 

my $searchRe = do { 
    my $searchCriteria = join '|', map "(?:$_)", @ARGV; 
    Regexp::Optimizer->new->optimize(qr($searchCriteria)i); 
}; 
+0

FWIW, ich denke Perls Regex-Engine handhabt alle diese Optimierungen automatisch jetzt. – jrockway

+0

Dies würde funktionieren, wenn er einen Verbindungstest mit || erstellen würde aber er verwendet && => "$ _ = ~/term_one/i && $ _ = ~/term_two/&& ..." – Axeman

+0

Eine schöne, schöne Übersetzung der Nachricht der geposteten Frage. Kudos. –

0

Sie könnten eine einzelne Regex erstellen, die funktionieren würde, WENN die Argumente keine Regex-Ausdrücke wären.

Die Idee ist folgende Reihenfolge:

  1. ein Wechsel (one|two|...)
  2. gefolgt von nicht-gierig allem (.*?)
  3. durch eine negative Vorschau gefolgt von dem, was angepasst Wechsel # 1 (?!\1)
  4. gefolgt von der wiederholte Wechsel (one|two|...)
  5. gefolgt von nicht-gierigen irgendetwas (.*?)
  6. durch eine negative Vorschau von einem Wechsel gefolgt von dem, was entweder des letzten Wechsel angepasst (?!\1|\2)

Wiederholen Sie 4-6, bis wir ein Spiel für alle Laufzeiten.

So ist der Code, der diese Konstrukte ist:

sub create_compound_match { 

    my @args   = @_; 
    my $arg_limit  = $#args; 
    my $expr   = join('|', map { lc; } @args); 
    my $neg_lookahead = join('|', map { "\\$_" } 1..$arg_limit); 

    my $test = join('.*?' 
        , "($expr)" 
        , (map { '(?!' 
          . substr($neg_lookahead, 0, 3 * $_ - 1) 
          . ")($expr)" 
          } 1..$arg_limit 
        ) 
        ); 

    return qr/$test/i; 
} 

, die auf diese Art und Weise getestet werden würde:

my $test = create_compound_match(@ARGV); 
print foreach grep { /$test/i } <$fh>; 

Dies ist jedoch die Fähigkeit von @ARGV in reguläre Ausdrücke auf der Kommandozeile übergeben fehlt könnte gleich qw sein, und die generierte Regex würde glücklicherweise mit 'aa aaa aaaa' übereinstimmen, ohne die Übereinstimmung von b* zu erfordern, weil "aa"! = "a", so dass das negative Lookahead erfüllt ist.

0

Nachdem ich Taking from Hynek -Pichi- Vychodil's answer angesehen habe, erkannte ich, dass Sie diese ganze Aufgabe mit List :: MoreUtils all Unterroutine wesentlich vereinfachen können. Ich empfehle dieses Paket sehr.

use strict; 
use warinings; 
use List::MoreUtil qw/all/; # if installed (not a bad idea to get it if not) 

my @regexen = map { qr/$_/i } @ARGV; 

open my $fh, '<', 'vm.txt' or die $!; 

foreach my $line (<$fh>) { 
    print $line if all { $line =~ $_ } @regexen; 
}; 

close $fh; 

Subsitute all mit any für ein "oder" Basis-Spiel.

Edit: Verdammt, sieht aus wie Axeman etwas sehr ähnliches geschrieben. Großartige Köpfe denken gleich, wie? :)

Verwandte Themen