匹配字符串数组以在perl中搜索的最简单方法?

时间:2010-06-11 01:49:42

标签: perl string search arrays comparison

我想要做的是检查我的搜索字符串中的字符串数组并获取相应的键,以便我可以存储它。使用Perl是否有一种神奇的方法,或者我注定要使用循环?如果是这样,最有效的方法是什么?

我对Perl相对较新(我只编写了2个其他脚本),所以我还不知道很多魔法,只是Perl是魔法= D

Reference Array: (1 = 'Canon', 2 = 'HP', 3 = 'Sony')
Search String: Sony's Cyber-shot DSC-S600
End Result: 3

4 个答案:

答案 0 :(得分:11)

<强>更新

根据this question中的讨论结果,根据您对“不使用循环”构成的意图/标准,下面基于map的解决方案(请参阅“选项#1 )可能是最简洁的解决方案,只要你不考虑map一个循环(答案的简短版本是:它是一个循环,就实现/性能而言,它不是一个循环来自语言理论观点)。


假设您不关心是否将“3”或“Sony”作为答案,您可以在一个简单的情况下通过使用“或”来构建正则表达式而无需循环“数组中的逻辑(|),如下所示:

my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
my $combined_search = join("|",@strings); 
my @which_found = ($search_in =~ /($combined_search)/); 
print "$which_found[0]\n";

我的测试运行结果:Sony

正则表达式(一旦变量$combined_search被Perl插值)采用你想要的形式/(Canon|HP|Sony)/

如果任何字符串包含正则表达式特殊字符(例如|)),这将无法正常工作 - 在这种情况下,您需要转义它们

注意:我个人认为这有点作弊,因为为了实现join(),Perl本身必须在interpeter内的某个地方做一个循环。因此,这个答案可能无法满足您保持无环路的愿望,这取决于您是否希望避免出现性能考虑的循环,以及更清晰或更短的代码。


P.S。要获得“3”而不是“索尼”,你将不得不使用一个循环 - 或者以明显的方式,通过在它下面的循环中进行1次匹配;或者使用一个库来节省您自己编写循环但会在调用下面有一个循环。

我将提供3种替代解决方案。

#1选项: - 我最喜欢的。使用“地图”,我个人仍然认为这是一个循环:

my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
my $combined_search = join("|",@strings); 
my @which_found = ($search_in =~ /($combined_search)/); 
print "$which_found[0]\n";
die "Not found" unless @which_found;
my $strings_index = 0;
my %strings_indexes = map {$_ => $strings_index++} @strings;
my $index = 1 + $strings_indexes{ $which_found[0] };
# Need to add 1 since arrays in Perl are zero-index-started and you want "3"

#2选项:使用隐藏在一个漂亮的CPAN库方法后面的循环:

use List::MoreUtils qw(firstidx);
my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
my $combined_search = join("|",@strings); 
my @which_found = ($search_in =~ /($combined_search)/); 
die "Not Found!"; unless @which_found;
print "$which_found[0]\n";
my $index_of_found = 1 + firstidx { $_ eq $which_found[0] } @strings; 
# Need to add 1 since arrays in Perl are zero-index-started and you want "3"

#3选项:以下是明显的循环方式:

my $found_index = -1;
my @strings = ("Canon", "HP", "Sony"); 
my $search_in = "Sony's Cyber-shot DSC-S600"; 
foreach my $index (0..$#strings) {
    next if $search_in !~ /$strings[$index]/;
    $found_index = $index;
    last; # quit the loop early, which is why I didn't use "map" here
}
# Check $found_index against -1; and if you want "3" instead of "2" add 1.

答案 1 :(得分:2)

这是一个使用嵌入式代码构建正则表达式的解决方案,以便在perl移动通过正则表达式时递增索引:

my @brands = qw( Canon HP Sony );
my $string = "Sony's Cyber-shot DSC-S600";

use re 'eval';  # needed to use the (?{ code }) construct

my $index = -1;
my $regex = join '|' => map "(?{ \$index++ })\Q$_" => @brands;

print "index: $index\n" if $string =~ $regex;

# prints 2 (since Perl's array indexing starts with 0)

每个品牌前面的字符串首先递增索引,然后尝试匹配品牌(使用quotemeta转义(作为\Q)以允许品牌名称中使用正则表达式特殊字符) 。

当匹配失败时,正则表达式引擎移动超过交替|,然后重复模式。

如果您要匹配多个字符串,请确保在每个字符串之前重置$index。或者,您可以将(?{$index = -1})添加到正则表达式字符串中。

答案 2 :(得分:1)

一种简单的方法就是使用哈希和正则表达式:

my $search = "your search string";
my %translation = (
    'canon' => 1,
    'hp'    => 2,
    'sony'  => 3
);

for my $key ( keys %translation ) {
    if ( $search =~ /$key/i ) {
        return $translation{$key};
    )
}

当然,回报也很容易成为印刷品。你也可以用while循环包围整个事物:

while(my $search = <>) {
    #your $search is declared = to <> and now gets its values from STDIN or strings piped to this script
}

请在perlre查看perl的正则表达式功能 并在perlref

查看perl的数据结构

修改

正如刚刚向我指出的那样,你试图避免使用循环。另一种方法是使用perl的map函数。看看here

答案 3 :(得分:0)

您还可以查看Regexp::Assemble,它将采用一组子正则表达式并从中构建一个超级正则表达式,然后可以用它们一次测试所有这些正则表达式(并给出你当然是匹配正则表达式的文本。我不确定这是最好的解决方案,如果你只想看到你想要匹配的三个字符串/正则表达式,但如果你有一个更大的目标集 - 我最初使用它的项目肯定是要走的路。有一个大约1500个术语的库,它与之匹配并且表现非常好。