0

给定文件:

A.hash.pl:

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

B.输入.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;

这段代码有什么问题?

4

3 回答 3

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
于 2012-06-01T16:27:58.727 回答
1

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

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

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

我还强烈建议您学习一些更现代的 Perl。如果您在程序中使用了这两行代码,您会立即发现问题:

use strict;
use warnings;

strict将确保您已通过my或定义了变量our。这样可以确保您不会说$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";

这不起作用,因为$highest在 if 语句中定义。您必须在声明$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 字符。它看起来并不多,但在许多情况下它可以不那么混乱。

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

这是用更现代的 Perl 风格重写的程序。我保留了您的标准算法,但添加了新的编译指示,在 ing%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;
于 2012-06-01T15:22:11.460 回答
0

您正在尝试重用 variables $1$2$3在另一个正则表达式中,我怀疑这就是把事情搞砸的原因。当我尝试你的代码时,我得到了错误:

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

因此,一种可能的解决方案是在捕获值后立即复制这些值,以避免在$1编译第二个正则表达式时 etc 变量被覆盖:

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

然后当然替换$1$args[0]等等。

您还应该知道,不运行脚本use warnings不是一个好主意。你认为通过懒惰节省的时间将比调试简单的错误少 10 倍。为什么要使用严格和警告?

于 2012-06-01T15:05:55.763 回答