【问题标题】:Perl - initialization of hashPerl - 哈希的初始化
【发布时间】:2016-10-24 22:16:39
【问题描述】:

我不确定如何正确初始化哈希 - 我正在尝试为输入文件中耦合线中的值创建键/值对。

例如,我的输入如下所示:

@cluster t.18
46421 ../../../output###.txt/
@cluster t.34
41554 ../../../output###.txt/

我从第 1 行(@cluster 行)中提取 t 编号,并将其与第二行(以 46421 开头的行)中的 output###.txt 匹配。但是,我似乎无法使用我编写的脚本将这些值放入我的哈希中。

#!/usr/bin/perl
use warnings;
use strict;

my $key;
my $value;
my %hash;

my $filename = 'input.txt';
open my $fh, '<', $filename or die "Can't open $filename: $!";

while (my $line = <$fh>) {
        chomp $line;
        if ($line =~ m/^\@cluster/) {
            my @fields = split /(\d+)/, $line;
            my $key = $fields[1];          
        }
        elsif ($line =~ m/^(\d+)/) { 
            my @output = split /\//, $line;
            my $value = $output[5];       
}          
        $hash{$key} = $value;
}

【问题讨论】:

  • if/elsif 块中从my $keymy $value 中删除my,因为这将创建 变量并隐藏全局变量。但预期的结果是什么? {1 =&gt; 'output###.txt', 2 =&gt; 'output###.txt'} ?
  • @PerlDog 感谢您的反馈。是的,我想要的哈希值是 {1 =&gt; 'output###.txt', 2 =&gt; 'output###.txt'} - 我只想添加一个计数器,但输入文件中的数字不按顺序排列。
  • 好的,第 1 行中的 1 与第 2 行中的 1111 有什么关系?它们为什么匹配?因为第 2 行以第 1 行数字的 4 倍开头,还是因为它是 number + 1110?还是仅仅因为它是下一行?请显示一些不那么模棱两可的输入。
  • @PerlDog,我试图简化我在问题中发布的输入,但这里是实际输入的前几行 @cluster t.10 has 4319 elements, 0 subclusters 37652 ../../../../clust/output43888.txt 1.397428 基本上第二行中的数字是一个任意标识符,那么它就是输出文件(output###.txt),最后是距离测量。
  • @EA00 不客气。你正在研究你提出的东西,吸收新事物,很高兴能提供一点帮助。正则表达式有点具体,我同意。 (而且他们也可以非常投入,变成一种单独的小语言。)我建议你把基础知识写下来,因为这是你每天都会使用的东西,只要你觉得舒服。这是您工具箱中应该包含的东西之一。

标签: arrays perl hash


【解决方案1】:

这是个好主意,但是您在if 中使用my 创建的$key 是一个作用域为该块的局部变量,屏蔽了全局$key。在 if 块内,符号 $key 与您预先声明的符号无关。见my in perlsub

一旦if 完成,此本地$key 就会超出范围,并且在if 块之外不存在。全局$keyif 之后再次可用,在循环的其他地方可见,但由于从未分配过而未定义。 elsif block 中的 $value 也是如此。

只需将my 声明放到循环中,从而分配给这些全局变量(如预期的那样?)。所以,$key = ...$value = ...,hash 会被正确分配。


注意——这是关于如何正确分配哈希值。我不知道您的实际数据看起来如何以及该行是否正确解析。这是一个玩具input.txt

@cluster t.1 1111 ../../../output1.1.txt/ @cluster t.2 2222 ../../../output2.2.txt/

我选择第 4 个字段而不是第 6 个字段,$value = $output[3];,然后添加

print "$_ => $hash{$_}\n" for keys %hash;

在循环之后。这打印

1 => output1.1.txt 2 => output2.2.txt

我不确定这是否是您想要的,但哈希构建得很好。


关于解析工具选择的评论

当捕获到分隔符时,您可以使用split 的属性来解析行中的数字。这很简洁,但从某种意义上说,它颠倒了它的主要目的,即从字符串中提取 other 组件,由模式分隔。因此,它可能会使代码的目的有点复杂,并且您还必须非常精确地索引以检索您需要的内容。

与其使用split 来提取由正则表达式给出的分隔符​​本身,为什么不使用正则表达式来提取它呢?这也使意图一清二楚。例如,带输入

@cluster t.10 有 4319 个元素,0 个子集群 37652 ../../../../clust/output43888.txt 1.397428

解析可以是这样的

if ($line =~ m/^\@cluster/) {
    ($key) = $line =~ /t\.(\d+)/;
}   
elsif ($line =~ m/^(\d+)/) { 
    ($value) = $line =~ m|.*/(\w+\.txt)|;
}    
$hash{$key} = $value if defined $key and defined $value;

添加t\.\.txt 以更精确地指定目标。如果目标字符串不确定具有那种精确的形式,只需捕获\d+,在第二种情况下,最后一个/ 之后的所有非空格,例如m|^\d+.*/(\S+)|。我们使用.* 的贪婪,它将一切 匹配到后面的东西(/),因此一直到最后的/

然后您也可以将其减少为每行的单个正则表达式,例如

if ($line =~ m/^\@cluster\s+t\.(\d+)/) {
    $key = $1;
}
elsif ($line =~ m|^\d+.*/(\w+\.txt)|) {
    $value = $1;
}

请注意,我已在哈希分配中添加了一个条件。实际上,原始代码在第一次迭代时分配了一个undef,因为那时还没有看到$value。这在下一次迭代中被覆盖,如果我们只在之后打印散列,我们就看不到它。该条件还可以防止匹配失败、格式错误的行等。当然,可以运行更好的检查。

【讨论】:

  • @PerlDog 拆分对我有用,可以提取每一行的数字和输出 - 我只是无法初始化哈希。但是,您建议放弃我的作品!谢谢!
  • @zdim 我试图弄清楚如何打印哈希 - 所以谢谢。另外,你的解释很有道理。
  • @PerlDog 公平点,谢谢你,我已经改变了冒犯性的陈述,说哈希是好的,而不是其他的。 (但是,请注意我添加的内容—— split 实际上会提取数字。)我也会修复解析。谢谢。
  • @PerlDog 相反。 split 可能很适合实际数据,但在我看来,它并不适合显示的内容 - 它依赖于 split 保持捕获的模式,然后按索引提取,从而逆转主要目的的split。如果我们想要数字,我不会 split 在数字和计数索引上——我认为正则表达式会更清晰。
  • @EA00 对于打印哈希(或任何其他数据结构),您可以查看Data::Dumper。它是为此目的而编写的核心模块。
猜你喜欢
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 2016-02-10
相关资源
最近更新 更多