如何在Perl字符串中扩展变量?

时间:2009-02-25 19:28:51

标签: regex perl string

我正在尝试在if条件中展开字符串$searchCriteria。有线索吗?

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 $_;
    }
}

6 个答案:

答案 0 :(得分:3)

如果您的搜索条件是正则表达式,则应准备自己编译的正则表达式。还要注意使用while循环(读取文件时)以避免过多的内存使用。如果您想要包含任何参数的行:

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;

或者如果你想要包含所有的行:

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;

(请注意,如果命令行参数是字符串而不是正则表达式,那么在\Q周围可能需要\E$_。)

最后,如果您希望提高许多搜索条件的速度,请使用Regexp::Optimizer

use Regexp::Optimizer;

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

答案 1 :(得分:1)

嗯......我不确定问题是什么......但不知怎的,我怀疑你需要qr //表达式(引用正则表达式)。

答案 2 :(得分:1)

看起来您想要评估该行是否包含@ARGV中的所有参数。

我认为这可能更符合您所寻找的内容,也更适合perl人群。

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;
}

当然,如果您想要进行一些压缩,因为您将在一次执行中一次又一次地执行测试,那么您可以为此创建一个子。

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 - $@" if $@;
    return $test_sub;
}

它与$_ =~ /blah/i && $_ =~ /blah2/ ...

的作用相同

那就是:

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

但我可能更有可能这样做:

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

......甚至......

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

答案 3 :(得分:0)

你的问题是什么?也许您正在寻找ack?看起来你正试图eval一个字符串(通常是一个坏主意)。你应该至少使用qr //。也许是这样的:

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->();
}

这很容易改进。它没有经过测试。 YMMV。

答案 4 :(得分:0)

如果参数不是正则表达式,您可以构造一个可以正常工作的正则表达式。

这个想法是以下顺序:

  1. 交替(one|two|...)
  2. 其次是非贪婪的(.*?)
  3. 接下来是对交替#1 (?!\1)
  4. 进行匹配的否定前瞻
  5. 然后重复交替(one|two|...)
  6. 其次是非贪婪的(.*?)
  7. 后面是与最后一次轮换(?!\1|\2)
  8. 中的任何一个匹配的替换的否定前瞻

    重复4-6,直到我们匹配所有条款。

    所以构造它的代码是:

    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;
    }
    

    将以这种方式测试:

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

    然而,这缺乏在命令行上传递正则表达式的能力@ARGV可能等于qw,并且生成的正则表达式将很乐意匹配'a aa aaa aaaa'而不需要匹配b* ,因为“aa”!=“a”,所以负面前瞻就满足了。

答案 5 :(得分:0)

在查看Taking from Hynek -Pichi- Vychodil's answer之后,我意识到您可以使用List :: MoreUtil的all子例程大大简化整个任务。我强烈推荐这个包。

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;

allany合并为“或”匹配。

编辑:该死的,看起来Axeman发布的内容非常类似于此。伟大的思想相似,嗯? :)