0

我有一个代码,它能够读取两个文件作为输入,并在输出中的两个写入匹配元素之间进行比较。让我们将脚本读取的 $list_file 视为用于从 $data_file 中选择元素的固定列表。我试图让这个脚本在一个文件夹中循环并读取具有特定名称模式的多个 data_files,但我遇到了一个错误,我无法解决它。

这是我的名为“list.txt”的列表文件:

X1 A B
X2 C D
X3 E F

我的第一个 data_file 名为“data_file1.txt”:

A X1 2 5
B X1 3 7
C X2 1 4
D X2 1 5

我的第二个 data_file 名为“data_file2.txt”:

E X3 5 7
F X3 3 4
G X4 2 3
H X4 2 5

我想获得如下所示的输出:

X1 A B 2 5 3 7
X2 C D 1 4 1 5
X3 E F 5 7 3 4

我的两个 data_files 都在一个包含其他文件的文件夹中,所以我需要匹配“数据”作为模式来识别正确的输入。

这是我的代码:

my $list_file = "list.txt";
my $dirname = "data_directory";
my $dh;

use strict;
use warnings;
use autodie;
use feature 'say';  

opendir ($dh, $dirname) || die "Impossible open the $dirname!";
while (my $data_file = readdir ($dh)){
    if ("$dh/$data_file" =~ /data/){

        open my $data_fh, "<", $data_file;
        my %data;
        while (<$data_fh>) {
            chomp;
            my ($id2, $id1, @data) = split /\t/;
            $data{$id1}{$id2} = \@data;
        }

        open my $list_fh, "<", $list_file;
        LINE: while(<$list_fh>) {
            chomp;
            my ($id1, @id2s) = split /\t/;
            my $data_id1 = $data{$id1};
            defined $data_id1 or next LINE;  

            my @values = map @{ $data_id1->{$_} }, @id2s;  
            say join "\t", $id1, @id2s, @values;
        }
    }
}
closedir (DIR);

如果我运行此代码,我只会为第一个 data_file 获得正确的结果。此外,我收到此错误:

"Can't open 'data_file2.txt' for reading: 'No such file or directory' at code.pl line 23"

欢迎提出建议!

4

4 回答 4

1
perl -ane '
  BEGIN{ open $I,shift or die $! }
  ($x, $y) = splice(@F,0,2);
  $h{$x}{$y} = "@F";
  END{ 
    @F=split, 
    s/$/ $h{ $F[1] }{ $F[0] } $h{ $F[2] }{ $F[0] }/, 
    print 
      while <$I>
  }
' list.txt *data*.txt

输出

X1 A B 2 5 3 7
X2 C D 1 4 1 5
X3 E F 5 7 3 4
于 2013-08-12T14:32:26.027 回答
0

您需要定义什么$fils_list是开始

$ perl -cw ff.pl 
Global symbol "$fils_list" requires explicit package name at ff.pl line 22.
ff.pl had compilation errors.
于 2013-08-12T14:03:57.180 回答
0

而不是这个: -

open my $data_fh, "<", $data_file;

也许你需要这个:-

open my $data_fh, "<", "$dh/$data_file"

查看文档

于 2013-08-12T14:05:06.127 回答
0

如果您要查找名称以 开头data_和结尾的所有文件的列表.txt,并且保证它们位于脚本工作目录正下方的目录树的单个级别中,您可以廉价地获得该列表

@data_files = glob('**/data_*.txt');

如果它们在单个目录中,例如“datapath”,那就更简单了;只是

@data_files = glob('datapath/data_*.txt');

一般来说,这是一个您不必自己解决的问题,因为 Perl 的实现者已经为您解决了;有关如何适应glob()您的具体情况的更多信息,请尝试perldoc -f glob,这将为您提供您可以忍受的所有细节。如果做不到这一点,您可能会考虑File::Find,这需要更多的努力,但提供了更大的交换灵活性;对于glob()无法解决的问题,File::Find可能应该是您使用的第一个工具。

但是,在这种情况下,假设以下目录结构

list-and-data/
list-and-data/list.txt
list-and-data/data/data_1.txt
list-and-data/data/data_2.txt
list-and-data/data/et-cetera.txt
list-and-data/data/something-else.txt

并且您只想选择 中的数据文件data/,您可以简单地:

sub read_file {
  my $filename = shift();
  my @data;
  open my $fh, '<', $filename
    or die "Can't open '$filename' to read: $!\n";
  @data = <$fh>
    or die "Failed to read '$filename', or empty: $!\n";
  close $fh;

  chomp foreach @data;
  return \@data;
};

my $list_file = "list.txt";
my $dirname = "data/";
my $data_filespec = "data_*.txt";

my @list = @{ read_file($list_file) };

my %data = {};
my @files = glob("$dirname/$data_filespec");

foreach my $file (@files) {
  my $basename = $file; $basename =~ s@^.*/@@;
  $data{$basename} = read_file($file);
};

此时,您拥有list.txtin@list的内容和所有数据文件 in 的内容%data,由文件的基本名称键入(例如,“list-and-data/data/data_1.txt”将由“data_1.txt”键入)。

这大大简化了您的生活;无需opendir()和朋友费心,您的文件读取代码都在一个地方,因此如果您需要对其行为方式进行任何更改,您不必在整个源文件中徘徊做同样的事情一遍又一遍。

类似地,您的所有数据都在一个位置,并以找到它的文件名作为关键字,因此分析的其余部分就像迭代一样简单keys %data

于 2013-08-12T14:29:05.203 回答