Perl,匹配目录的文件,使用包含这些文件名的一部分的数组
Perl, matching files of a directory, using an array with part of the these file names
所以,我有这个目录,其中包含这样命名的文件:
HG00119.mapped.ILLUMINA.bwa.GBR.low_coverage.20101123.bam.bai
HG00119.mapped.ILLUMINA.bwa.GBR.exome.20120522.bam_herc2_data.bam
HG00117.mapped.illumina.mosaik.GBR.exome.20110411.bam_herc2_phase1.bam
HG00117.mapped.illumina.mosaik.GBR.exome.20110411.bam.bai
NA20828.mapped.illumina.mosaik.TSI.exome.20110411.bam_herc2_phase1.bam
NA20828.mapped.ILLUMINA.bwa.TSI.low_coverage.20130415.bam_herc2_data.bam
我有一个 input.txt 文件,每一行都包含。
NA20828
HG00119
如您所见,input.txt 文件以目录内的文件名开头。
我想做的是过滤 input.txt 目录中具有名称(在本例中只是开头)的文件。
我不知道我是否清楚,但这是我到目前为止所做的代码。
use strict;
use warnings;
my @lines;
my @files = glob("*.mapped*");
open (my $input,'<','input.txt') or die $!;
while (my $line = <$input>) {
push (@lines, $line);
}
close $input;
我使用 glob 仅过滤名称中有映射的文件,因为那里还有我不想查找的其他文件。
我尝试了一些 foreach 循环,也尝试了 grep 和 regex,我很确定我的方向是正确的,我认为我的错误可能与范围有关。
我将不胜感激任何帮助!谢谢!
好的,首先 - 你的 while 循环是多余的。如果您从列表上下文中的文件句柄读取,它会读取整个内容。
my @lines = <$input>;
将执行与您的 while 循环相同的操作。
现在,对于您的模式 - 您将一个列表与另一个列表进行匹配,但只是部分匹配。
chomp ( @lines );
foreach my $file ( @files ) {
foreach my $line ( @lines ) {
if ( $file =~ m/$line/ ) { print "$file matches $line"; }
}
}
(是的,像 grep 或 map 这样的东西可以做到这一点,但我总是发现这两个让我头疼 - 它们更整洁,但它们隐式循环所以你并没有真正获得太多算法效率).
您可以像这样从 input.txt
的内容构建正则表达式
my @lines = do {
open my $fh, '<', 'input.txt' or die $!;
<$fh>;
};
chomp @lines;
my $re = join '|', @lines;
然后使用
找到需要的文件
my @files = grep /^(?:$re)/, glob '*.mapped*';
请注意,如果 input.txt
中的列表包含任何正则表达式元字符,例如 .
、*
、+
等,您需要对它们进行转义,可能像这样使用 quotemeta
my $re = join '|', map quotemeta, @lines;
而且最好还是这样做,除非您确定文件中永远不会有这样的字符。
所以,我有这个目录,其中包含这样命名的文件:
HG00119.mapped.ILLUMINA.bwa.GBR.low_coverage.20101123.bam.bai
HG00119.mapped.ILLUMINA.bwa.GBR.exome.20120522.bam_herc2_data.bam
HG00117.mapped.illumina.mosaik.GBR.exome.20110411.bam_herc2_phase1.bam
HG00117.mapped.illumina.mosaik.GBR.exome.20110411.bam.bai
NA20828.mapped.illumina.mosaik.TSI.exome.20110411.bam_herc2_phase1.bam
NA20828.mapped.ILLUMINA.bwa.TSI.low_coverage.20130415.bam_herc2_data.bam
我有一个 input.txt 文件,每一行都包含。
NA20828
HG00119
如您所见,input.txt 文件以目录内的文件名开头。
我想做的是过滤 input.txt 目录中具有名称(在本例中只是开头)的文件。 我不知道我是否清楚,但这是我到目前为止所做的代码。
use strict;
use warnings;
my @lines;
my @files = glob("*.mapped*");
open (my $input,'<','input.txt') or die $!;
while (my $line = <$input>) {
push (@lines, $line);
}
close $input;
我使用 glob 仅过滤名称中有映射的文件,因为那里还有我不想查找的其他文件。
我尝试了一些 foreach 循环,也尝试了 grep 和 regex,我很确定我的方向是正确的,我认为我的错误可能与范围有关。
我将不胜感激任何帮助!谢谢!
好的,首先 - 你的 while 循环是多余的。如果您从列表上下文中的文件句柄读取,它会读取整个内容。
my @lines = <$input>;
将执行与您的 while 循环相同的操作。
现在,对于您的模式 - 您将一个列表与另一个列表进行匹配,但只是部分匹配。
chomp ( @lines );
foreach my $file ( @files ) {
foreach my $line ( @lines ) {
if ( $file =~ m/$line/ ) { print "$file matches $line"; }
}
}
(是的,像 grep 或 map 这样的东西可以做到这一点,但我总是发现这两个让我头疼 - 它们更整洁,但它们隐式循环所以你并没有真正获得太多算法效率).
您可以像这样从 input.txt
的内容构建正则表达式
my @lines = do {
open my $fh, '<', 'input.txt' or die $!;
<$fh>;
};
chomp @lines;
my $re = join '|', @lines;
然后使用
找到需要的文件my @files = grep /^(?:$re)/, glob '*.mapped*';
请注意,如果 input.txt
中的列表包含任何正则表达式元字符,例如 .
、*
、+
等,您需要对它们进行转义,可能像这样使用 quotemeta
my $re = join '|', map quotemeta, @lines;
而且最好还是这样做,除非您确定文件中永远不会有这样的字符。