循环在一个文件夹中读取具有特定模式的文件...获取错误perl

时间:2013-08-12 13:45:50

标签: perl

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

这是我的名为“list.txt”的list_file:

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都在一个包含其他文件的文件夹中,所以我需要将“data”作为模式匹配以识别正确的输入。

这是我的代码:

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 个答案:

答案 0 :(得分: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

答案 1 :(得分: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.

答案 2 :(得分:0)

而不是: -

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

也许你需要这个: -

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

请参阅docs

答案 3 :(得分:0)

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

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

如果他们在一个目录中,例如“数据通路”,这更简单;刚

@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.txt中的@list内容以及%data中所有数据文件的内容都以文件的基本名称键入(例如“list-and -data / data / data_1.txt“将由”data_1.txt“键入。

这大大简化了你的生活;没有必要打扰opendir()和朋友,你的文件阅读代码都集中在一个地方,所以如果你需要对它的行为进行任何改变,你就不必在整个源头漫游文件一遍又一遍地做同样的事情。

同样地,您的所有数据都在一个地方,有助于通过找到它的文件名键入,因此分析的其余部分就像迭代keys %data一样简单。