鉴于文件:
一个。 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;
此代码有什么问题?
答案 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
会确保您已通过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";
这不起作用,因为在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
而不会相互干扰。
我之所以要这样做是因为你require
。 my
变量仅在其范围内可用。也就是说,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?