【问题标题】:Simplest way to match array of strings to search in perl?匹配字符串数组以在 perl 中搜索的最简单方法?
【发布时间】:2011-03-02 11:21:59
【问题描述】:

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

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

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

【问题讨论】:

  • Perl 并不是真正的魔法。这只是 Arthur C. Clarke 的先进技术的一个例子,它与魔法无法区分 :) 再说一次,我个人认为这整个格式是巫毒 :(
  • 最近讨厌这种循环是怎么回事?如果您需要对元素列表执行某些操作,则必须以某种方式遍历它们。您可能没有明确使用forwhile,但归根结底,即使是最深奥的解决方案也会在后台使用某种循环。
  • @kemp - 最近有没有其他我错过的反循环问题?

标签: perl string search arrays comparison


【解决方案1】:

更新:

基于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 本身必须在解释器内部的某个地方执行循环。因此,此答案可能无法满足您保持无循环的愿望,具体取决于您是否希望出于性能考虑而避免循环,或者是否希望代码更简洁或更短。


附:要获得“3”而不是“Sony”,您必须使用循环 - 以一种明显的方式,通过在其下方的循环中进行 1 次匹配;或者使用一个库来避免你自己编写循环,但在调用下面会有一个循环。

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

#1 选项: - 我的最爱。使用“map”,我个人仍然认为是循环:

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.

【讨论】:

  • 感谢您提供详细而翔实的答案:D 此信息写得很好,很有用。
  • 我还有一个与此相关的问题。我想用一个二维值数组来搜索它,但我不知道如何用除了选项 3 之外的任何方法来做到这一点。对此有什么建议吗? (我编辑了问题以反映新数组)
  • @Ben - 您可能希望将其创建为一个新问题...(链接到这个问题),这样人们就可以在可搜索性方面受益。
【解决方案2】:

一个简单的方法就是使用散列和正则表达式:

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

【讨论】:

  • OP 明确指出“或者我注定要使用循环?” - 对我来说,这听起来像是他知道他可以循环执行并且正在寻找一个非循环的答案。我可能误读了他
【解决方案3】:

这是一个使用嵌入式代码构建正则表达式的解决方案,以在 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})

【讨论】:

    【解决方案4】:

    您还可以查看Regexp::Assemble,它将获取子正则表达式的集合并从中构建单个超级正则表达式,然后可用于一次测试所有它们(并为您提供当然,匹配正则表达式的文本)。如果您只查看要匹配的三个字符串/正则表达式,我不确定它是否是最佳解决方案,但如果您有一个更大的目标集,这绝对是最好的解决方案 - 我最初使用它的项目有一个包含大约 1500 个术语的库,可以与之匹配,并且表现非常好。

    【讨论】:

      猜你喜欢
      • 2011-03-03
      • 2011-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-16
      • 1970-01-01
      • 2016-11-22
      • 1970-01-01
      相关资源
      最近更新 更多