将两个文件中的哈希值合并为一个文件

时间:2014-02-10 19:46:22

标签: arrays perl hash push dereference

我有两个文件包含这样的数据:

FILE1包含组号(第一列)和切换另一组(第二列)的频率(第三列):

FILE1:

1 2 0.6 
2 1 0.6
3 1 0.4
1 3 0.4
2 3 0.2

等...

FILE2包含组号(第一列)及其出现频率(第二列)。

FILE2:

1 0.9
2 0.7
3 0.5

等...

我想创建另一个包含FILE2的文件,其中包含来自FILE1的每个开关的值,如下所示:

1 0.9 2 0.6 3 0.4 ...
2 0.7 1 0.6 3 0.2 ...

基本上,我希望第一列是组号,第二列是它出现的频率,然后是它们切换到的组和该开关的频率,然后接下来切换该特定组的同一行,然后是第2组等。

所以我想在FILE1中读取,为每个组创建一个数组哈希,其中键是组号,值是他们切换到的组以及该开关的频率。我将为每个组包含一个大数组,其中包含他们切换到的每个组的子阵列和频率。然后我想用与第一个哈希中相同的键创建另一个哈希,但是使用来自FILE2中第一列的数字和来自FILE2第二列的值。然后我将打印出“hash2 key hash2 value hash1 full array for the key”。这是我尝试使用Perl:

#!/usr/bin/perl -W

$input1= $ARGV[0];
$input2 = $ARGV[1];
$output = $ARGV[2];

%switches=();

open (IN1, "$input1");
while (<IN1>) {
 @tmp = split (/\s+/, $_);
 chomp @tmp;
 $group = shift @tmp;
 $switches{$group} = [@tmp];

 push (@{$switches{$group}}, [@tmp]);

}

close IN1;

%groups=();

open (IN2, "$input2");
while (<IN2>) {
 chomp $_;
 ($group, $pop) = split (/\s+/, $_);
 $groups{$group} = $pop;
}
close IN2;

open (OUT, ">$output");

foreach $group (keys %groups) {
  print OUT "$group $pop @{$switches{$group}}\n"
}

close OUT;

我得到的输出包含:

1 0.1 2 0.1 ARRAY(0x100832330) 
2 0.3 5 0.2 ARRAY(0x1008325d0)

基本上是这样的:

“组”“最后一个频率号码”“该组切换到的最后一组”“最后一个开关频率”“如ARRAY(0x100832330)”

我认为我在使用FILE1时将所有开关推入数组的散列并且在打印时最后使用解除引用来解决问题。

请帮忙, 谢谢!

3 个答案:

答案 0 :(得分:0)

您的%switches哈希包含冗余信息;只需使用push即可。此外,您需要做更多工作来打印出您想要的内容。这是您的代码,只有很少的更改:

$input1= $ARGV[0];
$input2 = $ARGV[1];
$output = $ARGV[2];

%switches=();

open (IN1, "$input1");
while (<IN1>) {
 @tmp = split (/\s+/, $_);
 chomp @tmp;
 $group = shift @tmp;

 push (@{$switches{$group}}, [@tmp]);

}

close IN1;

%groups=();

open (IN2, "$input2");
while (<IN2>) {
 chomp $_;
 ($group, $pop) = split (/\s+/, $_);
 $groups{$group} = $pop;
}
close IN2;

open (OUT, ">$output");

foreach $group (sort {$a <=> $b} keys %groups) {
    print OUT "$group $groups{$group} ";
    for my $aref (@{$switches{$group}}) {
        print OUT "@{$aref}";
    }
    print OUT "\n";
}

close OUT;


__END__


1 0.9 2 0.63 0.4
2 0.7 1 0.63 0.2
3 0.5 1 0.4

另请参阅perldoc perldscperldoc Data::Dumper

答案 1 :(得分:0)

由于每列代表一些有价值的东西,而不是数组,因此您应该将数据存储在更详细的结构中。您可以通过references in Perl完成此操作。

引用是指向另一个数据结构的指针。例如,您可以将组存储在哈希中。但是,不是每个散列值包含由空格分隔的一串数字,而是每个散列值指向包含该组数据点的数组。并且,该数组中的每个数据点都指向一个哈希,其键为SWITCH代表其切换FREQ的频率。< / p>

您可以将第1组第一个数据点的频率说成:

$data{1}->[0]->{FREQ};

这样,您可以更轻松地操作数据 - 即使您只是将其重写为另一个平面文件。您还可以使用Storable模块以保存其结构的方式编写数据。

#! /usr/bin/env perl
#
use strict;
use feature qw(say);
use autodie;
use warnings;
use Data::Dumper;

use constant {
    FILE1       => "file1.txt",
    FILE2       => "file2.txt",
};

my %data;  # A hash of an array of hashes (superfun!)

open my $fh1, "<", FILE1;

while ( my $line = <$fh1> ) {
    chomp $line;
    my ( $group, $switch, $frequency ) = split /\s+/, $line;
    if ( not exists $data{$group} ) {
        $data{$group} = [];
    }
    push @{ $data{$group} }, { SWITCH => $switch, FREQ => $frequency };
}
close $fh1;

open my $fh2, "<", FILE2;
while ( my $line = <$fh2> ) {
    chomp $line;
    my ( $group, $frequency ) = split /\s+/, $line;
    if ( not exists $data{$group} ) {
        $data{$group} = [];
    }
    push @{ $data{$group} }, { SWITCH => undef, FREQ => $frequency };
}
close $fh2;
say Dumper \%data;

这会给你:

$VAR1 = {
        '1' => [
                {
                    'SWITCH' => '2',
                    'FREQ' => '0.6'
                },
                {
                    'SWITCH' => '3',
                    'FREQ' => '0.4'
                },
                {
                    'SWITCH' => undef,
                    'FREQ' => '0.9'
                }
                ],
        '3' => [
                {
                    'SWITCH' => '1',
                    'FREQ' => '0.4'
                },
                {
                    'SWITCH' => undef,
                    'FREQ' => '0.5'
                }
                ],
        '2' => [
                {
                    'SWITCH' => '1',
                    'FREQ' => '0.6'
                },
                {
                    'SWITCH' => '3',
                    'FREQ' => '0.2'
                },
                {
                    'SWITCH' => undef,
                    'FREQ' => '0.7'
                }
                ]
        };

答案 2 :(得分:0)

这将满足您的需求。

我为缺乏分析而道歉,但现在已经很晚了,我应该在床上。

我希望这会有所帮助。

use strict;
use warnings;

my $fh;
my %switches;

open $fh, '<', 'file1.txt' or die $!;
while (<$fh>) {
  my ($origin, @switch) = split;
  push @{ $switches{$origin} }, \@switch;
}

open $fh, '<', 'file2.txt' or die $!;
while (<$fh>) {
  my ($origin, $freq) = split;
  my $switches = join ' ', map join(' ', @$_), @{ $switches{$origin} };
  print join(' ', $origin, $freq, $switches), "\n";
}

<强>输出

1 0.9 2 0.6 3 0.4
2 0.7 1 0.6 3 0.2
3 0.5 1 0.4

<强>更新

以下是您自己的代码的固定版本,它会产生类似的结果。主要问题是%switches数组数组中的值,因此您必须进行两次解引用。我已经通过添加@switches来修复它,它包含与当前%switches值相同的内容,但是使用字符串代替双元素数组。

我还添加了use strictuse warnings,并正确地声明了所有变量。 open调用已更改为具有词汇文件句柄的三参数open,并且现在正在检查它们是否成功。我已经更改了您的split来电,因为您只需要一个没有参数的简单裸split。我已删除您的@tmp并使用了正确的列表分配。哦,我已将浪费的[@array]更改为简单的\@array(如果不使用my声明变量,则无效。)

我仍然认为我的版本更好,只是因为它更短,你的版本以随机顺序打印。

#!/usr/bin/perl

use strict;
use warnings;

my ($input1, $input2, $output) = @ARGV;

my %switches;

open my $in1, '<', $input1 or die $!;
while (<$in1>) {
  my ($group, @switches) = split;
  push @{ $switches{$group} }, \@switches;
}

close $in1;

my %groups;

open my $in2, '<', $input2 or die $!;
while (<$in2>) {
 my ($group, $pop) = split;
 $groups{$group} = $pop;
}
close $in2;

open my $out, '>', $output or die $!;
for my $group (keys %groups) {
  my $pop = $groups{$group};
  my @switches = map "@$_", @{ $switches{$group} };
  print $out "$group $pop @switches\n"
}
close $out or die $!;