【问题标题】:perl parsing and assigning multiple values to hash key using arrayperl使用数组解析和分配多个值给哈希键
【发布时间】:2013-06-04 16:24:13
【问题描述】:

我有一些文件,我正试图解析并从第三个文件构建哈希和查找。文件格式:

文件 1:

ID2
ID4

文件 2:

x1 y1 z1 ID1
x2 y2 z2 ID2
x3 y3 z3 ID2
x4 y4 z4 ID4

文件 3:

a1 b1
a2 b2
a3 b3

我正在尝试做的事情:

对于 File1 中的所有这些 ID,使用 File2 中的 ID 字段查找 x 和 y 坐标,并查看 File3 中的“a”是否位于 x 和 y 之间。

到目前为止我的想法:

  1. 获取文件 2;将其解析为以 ID 为键的哈希
  2. 获取文件 1;如果 ID 存在于文件 2 中;打开文件 3 并检查“a”的坐标范围 并打印出来

我执行了多远? 不太远。我正在尝试读取文件 2 并解析哈希中的所有元素,但我被卡住了:

while (<FILE>){
    chomp $_;
    my $line = $_;
    my @arr  = split ("\t", $line);
    my $id = $arr[3];

    if (exists ($hash{$id})) {
        my $x = $arr[0];
        my $y  = $arr[1];
        my $z   = $arr[2];
        push @{$hash{$id}{'x'}, $x;
        push @{$hash{$id}{'y'}, $y;
        push @{$hash{$id}{'y'}, $y;
    } else {
        $hash{'id'} = $id;
        $hash{$id}{'x'} = $arr[0];   
        $hash{$id}{'y'} = $arr[1];
        $hash{$id}{'z'} = $arr[2];
    }
}
print Dumper %hash;
close FILE;

当然,我在这里做错了

【问题讨论】:

  • 我用我的小眼睛窥探 (1) 语法错误@{$hash{$id}{'y'} ← 取消引用在哪里停止? (2) 一个y$y,其中应该有z/$z (3) 在else 中,您将$hash{id} 定义为一个字符串,然后将其用作包含标量。但是在if 中,您将它们视为arrayrefs 的hashrefs(我猜)。也许您应该向我们展示预期的数据结构。
  • 抱歉,这只是最初的想法。当涉及到数组的哈希时,我并不熟悉,所以请原谅语法错误。预期的数据结构应如下所示,其中考虑了同一 ID 的 x 、 y 和 z 的多个值。 $VAR58477 = 'ID2'; $VAR58478 = { 'x' => [ 'x2','x3, ], 'y' => [ 'y2','y3' ], 'z' => [ 'z2','z3' ] };

标签: arrays perl file-io hash


【解决方案1】:

这是将您的file2 读入哈希的方法。请注意,我认为使用三元素数组来保存 x、y 和 z 值可能比哈希值更容易。

我会展示更多,但我不清楚您的 file3 是如何工作的,以及它与 file1 的关系如何。是否要处理 file1 中的所有值并为每个值查找 file3 中的哪些值在相应的限制之间?

use strict;
use warnings;
use autodie;

use Data::Dump;

open my $fh, '<', 'file2.txt';

my %data;

while (<$fh>){
    chomp;
    my @fields = split /\t/;
    my $id = pop @fields;
    for ('x' .. 'z') {
      push @{$data{$id}{$_}}, shift @fields;
    }
}

dd \%data;

输出

{
  ID1 => { x => ["x1"], y => ["y1"], z => ["z1"] },
  ID2 => { x => ["x2", "x3"], y => ["y2", "y3"], z => ["z2", "z3"] },
  ID4 => { x => ["x4"], y => ["y4"], z => ["z4"] },
}

更新

虽然上面代码中的存储格式是我认为你想要的,但我认为它不是很可行。如果你使用这个,我认为你编写程序的其余部分会更容易

while (<$fh>){
    chomp;
    my @fields = split /\t/;
    my $id = pop @fields;
    push @{$data{$id}}, \@fields;
}

导致这个

{
  ID1 => [["x1", "y1", "z1"]],
  ID2 => [["x2", "y2", "z2"], ["x3", "y3", "z3"]],
  ID4 => [["x4", "y4", "z4"]],
}

甚至这个

while (<$fh>){
    chomp;
    my @fields = split /\t/;
    my $id = pop @fields;
    my %item;
    @item{qw/ x y z /} = @fields;
    push @{$data{$id}}, \%item;
}

产生这个数据

{
  ID1 => [{ x => "x1", y => "y1", z => "z1" }],
  ID2 => [
           { x => "x2", y => "y2", z => "z2" },
           { x => "x3", y => "y3", z => "z3" },
         ],
  ID4 => [{ x => "x4", y => "y4", z => "z4" }],
}

【讨论】:

  • 可能值得注意的是,ID2 的其中一个将在此处从file2 丢失。
  • @chrsblck:是的,我没有发现。
  • 是的,我同意 chrsblck,最大的挑战之一是如何为同一个 ID(例如 ID2)保留多个 x、y、z,这就是我想到使用数组的原因
  • @user2199881:我已将其更改为我认为您的原始代码的意图,但我认为这不是一个好主意。
  • 另外,就 File3 而言,我添加了另一列。在这里,我需要检查 a 是否与 'x' 相同,以及 'b' 是否介于 'y' 和 'z' 之间(两者都是数字)。在某种程度上,我正在尝试使用 ID 加入文件 1 和文件 2,然后查找文件 3 条目
【解决方案2】:

我会这样处理你的任务:

  1. 将文件 1 加载到内存中并作为 ID 过滤器保存

  2. 绑定文件 3 或将其加载到内存中,否则保留它以供索引

  3. 将文件 2 处理为请求流。

因此:

#! /usr/bin/env perl
use common::sense;
use Tie::File;
use autodie;

tie my @table, 'Tie::File', 'f3' or die $!;

my %filter;
open my $f, '<', 'f1';
while (<$f>) {
  chomp;
  $filter{$_}++
}
close $f;

while (<>) {
  next unless /^x(\d+) y(\d+) z\d+ (ID\d+)$/;
  next unless exists $filter{$3};
  say((split ' ', $table[$2])[$1])
}

untie @table;

用法:

$ ./example
x1 y1 z2 ID2
b2
x0 y0 z5 ID2
a1

$ ./example file2
<three blank lines, because your examples are 1-indexed>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多