我想要做的是检查我的搜索字符串中的字符串数组并获取相应的键,以便我可以存储它。使用Perl是否有一种神奇的方法,或者我注定要使用循环?如果是这样,最有效的方法是什么?
我对Perl相对较新(我只编写了2个其他脚本),所以我还不知道很多魔法,只是Perl是魔法= D
Reference Array: (1 = 'Canon', 2 = 'HP', 3 = 'Sony')
Search String: Sony's Cyber-shot DSC-S600
End Result: 3
答案 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个术语的库,它与之匹配并且表现非常好。