Perl:将数组的散列与另一个数组进行比较

时间:2012-06-01 14:16:00

标签: arrays perl hash

鉴于文件:

一个。 hash.pl:

%h1 = (
  A=>['4631 4576','6646 6646',],
  B=>['3539 4576',],
);

B中。 input.txt中

4576    4631    4
4576    3539    4

我必须编写一个Perl代码,在input.txt中找到值(4631 4576)。 (顺序并不重要。)这里,'4631 4576'在input.txt中显示为4576 4631。

我编写了以下代码,但似乎存在一些问题:

#!/usr/bin/perl -w
open (FH, "input.txt") or die "can't open file: &! \n";
require "hash.pl";
foreach $amp (<FH>)
{
    if ($amp=~/(\d+)\t(\d+)\t(\d+)/)        
    {       
        foreach $keys (keys %h1)
        {
            @tmparray= @{$h1{$keys}};
            foreach $tmp1 (@tmparray)
            {
                if ($tmp1 =~ m/($1 $2|$2 $1)/ )
                {
                    print "$keys", "$3\n";
                }
            }
        }
    }
}
close (FH);
exit;

此代码有什么问题?

3 个答案:

答案 0 :(得分:2)

此解决方案优先使用do require,因为后者用于包含库源文件,并在此上下文中返回无用的标量值。 do只返回执行的最后一个语句的值,因此可用于初始化局部变量。

该程序不是使用正则表达式,而是调用split来收集文件中的非空白字段。然后它检查有三个并且它们都是数字。

split的结果放入数组可以避免捕获的正则表达式字段丢失的问题。

构建正则表达式$re,允许前两个字段按任意顺序出现,然后在每个哈希元素上调用grep以验证哈希值数组中是否有任何值匹配此文件条目。

输出似乎很小,但它包含的信息与显示的原始代码相同。

use strict;
use warnings;

my %data = do 'hash.pl';

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

while (<$fh>) {

  my @values = split;
  next if grep /\D/, @values or @values != 3;

  my $re = qr/\A$values[0]\s+$values[1]\z|\A$values[1]\s+$values[0]\z/;

  foreach my $key (keys %data) {
    print "$key - $values[2]\n" if grep $_ =~ $re, @{$data{$key}};
  }
}

<强>输出

A - 4
B - 4

答案 1 :(得分:1)

问题很简单:您在程序中使用$1$2$3,但是当您使用它们时,您已经失去了它们的价值。这些是全局符号,只要您使用正则表达式运算符,它们就会被替换。在第一次正则表达式匹配后,只需将它们保存在另一个变量中:

$first  = $1;
$second = $2;
$third  = $3;

您还应该注意正则表达式。你的正则表达式有效,但它们非常非常狭窄。我第一次在你的文件中有标签时错过了它。我喜欢将\s+用于任何类型的空格。这将涵盖多个标签或空格或不同组合。

我还强烈建议你学习更多modern Perl。如果你在程序中使用了这两行,你会立即解决问题:

use strict;
use warnings;

strict会确保您已通过myour定义了变量。这样可以确保您没有说出$Foo一个地方和$foo另一个地方,并想知道您在$foo中存储的值发生了什么。

当您进行第二次正则表达式匹配时,warnings会立即突出显示$1$2没有值。

由于require,当你使用strict时,变量声明中的东西有点粘。 my变量是一个范围有限的严格局部变量。这就是99%的时间使用它的原因。

my变量仅存在于其声明的范围内。例如,如果在循环内声明变量,则它不存在于循环外:

if ($a > $b) {
    my $highest = $a;
} 
else {
    my $highest = $b;
}
print "The highest value is $highest\n";

这不起作用,因为在if语句中定义了$highest。您必须在声明之外声明$highest才能使其正常工作:

my $highest;
if ($a > $b) {
    $highest = $a;
} 
else {
    $highest = $b;
}
print "The highest value is $highest\n";

our声明的变量全局可用于整个。您可以在任何地方定义它 - 在循环内,在if语句中,在任何地方 - 并且稍后将可用。

包只是一个命名空间。除非你另有声明,否则你总是在main包中。防止模块变量影响代码中的变量很有用。这样,您包含的模块可以使用变量$foo,您可以使用变量$foo而不会相互干扰。

我之所以要这样做是因为你requiremy变量仅在其范围内可用。也就是说,for循环,if语句或整个文件。请注意最后一个:整个文件。这意味着如果我my %h1,它将不存在于文件之外。因此,我必须用our声明它。

此外,当您使用strict时,它非常严格。当它看到一个尚未声明的变量时,它会生成编译时错误。因此,我必须在主程序中声明%h1,因此编译器知道它。

我还使用了从say获得的use feature qw(say);语句。它就像print,除了它总是打印一个NL字符。它似乎并不多,但在许多情况下它可能不那么混乱。

现在强烈建议您使用声明的标量来打开文件而不仅仅是文件句柄。文件句柄是全局的,可能会导致问题。另外,在子例程中很难使用文件句柄。此外,建议使用三部分公开声明。这可以防止文件名以>|开头时出现问题。

这是用更现代的Perl风格重写的程序。我保留了您的标准算法,但添加了新的编译指示,在%h1之前声明require,并使用了更标准的open。否则,它几乎就是你所拥有的。

#! /usr/bin/env perl
#

use strict;
use warnings;
use feature qw(say);

our %h1;
require "hash.pl";


open ( my $input_fh, "<", "input.txt" )
    or die "can't open file: $! \n";

foreach my $amp ( <$input_fh> ) {
    chomp $amp;
    if ( $amp =~ /(\d+)\s+(\d+)\s+(\d+)/ ) {
        # Got to save the $1, $2, and $3 for later
        my $first = $1;
        my $second = $2;
        my $third = $3;
        foreach my $key ( keys %h1 ) {
            foreach my $tmp1 ( @{$h1{$key}} ) {
                if ($tmp1 =~ /($first\s+$second|$second\s+$first)/ ) {
                    say qq("$key": "$third");
                }
            }
        }
    }
}
close $input_fh;

答案 2 :(得分:0)

您正在尝试在另一个正则表达式中重用变量$1$2$3,我怀疑这是弄乱的事情。当我尝试您的代码时,我收到错误:

Use of uninitialized value $2 in regexp compilation ...

所以一个可能的解决方案是在捕获它们之后立即复制它们,以避免在编译第二个正则表达式时覆盖$1等变量:

if ($amp=~/(\d+)\t(\d+)\t(\d+)/) {       
    my @args = ($1,$2,$3);

然后当然用$1替换$args[0],依此类推。

您还应该知道,运行没有use warnings的脚本不是一个好主意。你认为通过懒惰保存的时间将比调试简单错误丢失10倍。 Why use strict and warnings?