我需要合并3个或多个文件,数据看起来像这样..
file 1
0334.45656
0334.45678
0335.67899
file 2
0334.89765
0335.12346
0335.56789
file 3
0334.12345
0335.45678
0335.98764
文件4中的预期输出
0334.89765
0334.89765
0334.89765
0334.12345
0335.67899
0335.12346
0335.56789
0335.45678
0335.98764
到目前为止,我已尝试过,但4rth文件中的数据不按排序顺序排列,
#!/usr/bin/perl
my %hash;
my $outFile = "outFile.txt";
foreach $file(@ARGV)
{
print "$file\n";
open (IN, "$file") || die "cannot open file $!";
open (OUT,">>$outFile") || die "cannot open file $!";
while ( <IN> )
{
chomp $_;
($timestamp,$data) = split (/\./,$_);
$hash{$timeStamp}{'data'}=$data;
if (defined $hash{$timeStamp})
{
print "$_\n";
print OUT"$_\n";
}
}
}
close (IN);
close (OUT);
答案 0 :(得分:3)
我通常不建议这样做,但unix实用程序应该能够处理这个问题。
cat
3个文件在一起。sort
对合并文件进行排序。但是,使用perl,可以执行以下操作:
#!/usr/bin/perl
use strict;
use warnings;
my @data;
push @data, $_ while (<>);
# Because the numbers are all equal length, alpha sort will work here
print for sort @data;
但是,正如我们所讨论的那样,文件可能非常大。因此,如果您能够利用所有文件已经排序的事实,它在内存和速度方面都会更有效。
以下解决方案因此流式传输文件,按顺序拉出下一个文件:
#!/usr/bin/perl
# Could name this catsort.pl
use strict;
use warnings;
use autodie;
# Initialize File handles
my @fhs = map {open my $fh, '<', $_; $fh} @ARGV;
# First Line of each file
my @data = map {scalar <$_>} @fhs;
# Loop while a next line exists
while (@data) {
# Pull out the next entry.
my $index = (sort {$data[$a] cmp $data[$b]} (0..$#data))[0];
print $data[$index];
# Fill In next Data at index.
if (! defined($data[$index] = readline $fhs[$index])) {
# End of that File
splice @fhs, $index, 1;
splice @data, $index, 1;
}
}
答案 1 :(得分:2)
以更可重复使用的方式使用米勒的想法,
use strict;
use warnings;
sub get_sort_iterator {
my @fhs = map {open my $fh, '<', $_ or die $!; $fh} @_;
my @d;
return sub {
for my $i (0 .. $#fhs) {
# skip to next file handle if it doesn't exists or we have value in $d[$i]
next if !$fhs[$i] or defined $d[$i];
# reading from $fhs[$i] file handle was success?
if ( defined($d[$i] = readline($fhs[$i])) ) { chomp($d[$i]) }
# file handle at EOF, not needed any more
else { undef $fhs[$i] }
}
# compare as numbers, return undef if no more data
my ($index) = sort {$d[$a] <=> $d[$b]} grep { defined $d[$_] } 0..$#d
or return;
# return value from $d[$index], and set it to undef
return delete $d[$index];
};
}
my $iter = get_sort_iterator(@ARGV);
while (defined(my $x = $iter->())) {
print "$x\n";
}
输出
0334.12345
0334.45656
0334.45678
0334.89765
0335.12346
0335.45678
0335.56789
0335.67899
0335.98764
答案 2 :(得分:1)
假设每个输入文件都已按升序排列并且其中至少有一行,此脚本可以按升序合并它们:
#!/usr/bin/perl
use warnings;
use strict;
use List::Util 'reduce';
sub min_index {
reduce { $_[$a] < $_[$b] ? $a : $b } 0 .. $#_;
}
my @fhs = map { open my $fh, '<', $_; $fh } @ARGV;
my @data = map { scalar <$_> } @fhs;
while (@data) {
my $idx = min_index(@data);
print "$data[$idx]";
if (! defined($data[$idx] = readline $fhs[$idx])) {
splice @data, $idx, 1;
splice @fhs, $idx, 1;
}
}
注意:这与@Miller提供的第二个脚本基本相同,但更清晰,更简洁。
答案 3 :(得分:0)
我建议使用这个解决方案,它使用一个排序的哈希数组 - 每个哈希对应一个输入文件,并包含一个文件句柄fh
,最后一行读取line
以及从中提取的时间戳第timestamp
行。
数组末尾的散列始终对应于时间戳值最小的输入,因此所有必要的是重复pop
数组中的下一个值print
它的数据,读取下一行和(如果它没有达到eof)按排序顺序将它插回到数组中。
对于其他答案使用的每个输出行的所有数据的重复排序,这可以产生明显的速度提升。
请注意,程序期望输入文件列表作为命令行上的参数,并将其合并的输出发送到STDOUT。它还假定输入文件已经排序。
use strict;
use warnings;
use autodie;
my @data;
for my $file (@ARGV) {
my $item;
open $item->{fh}, '<', $file;
insert_item($item, \@data);
}
while (@data) {
my $item = pop @data;
print $item->{line};
insert_item($item, \@data);
}
sub insert_item {
my ($item, $array) = @_;
return if eof $item->{fh};
$item->{line} = readline $item->{fh};
($item->{timestamp}) = $item->{line} =~ /^(\d+)/;
my $i = 0;
++$i while $i < @$array and $item->{timestamp} < $array->[$i]{timestamp};
splice @$array, $i, 0, $item;
}
<强>输出强>
0334.45656
0334.89765
0334.12345
0334.45678
0335.12346
0335.45678
0335.67899
0335.56789
0335.98764