我已经编写了一些用于处理FASTA / FASTQ文件的脚本(例如fastx-length.pl),但是希望使它们更通用,并同时接受压缩和未压缩文件作为命令行参数和标准输入(以便脚本&#34;只是工作&#34;当你向它们扔掉随机文件时)。对于我来说,在未压缩和压缩文件(例如压缩读取文件,未压缩的组合基因组)上工作是很常见的,而<(zcat file.fastq.gz)
之类的插件很快就会烦人。
这是fastx-length.pl
脚本中的一个示例块:
...
my @lengths = ();
my $inQual = 0; # false
my $seqID = "";
my $qualID = "";
my $seq = "";
my $qual = "";
while(<>){
chomp; chomp; # double chomp for Windows CR/LF on Linux machines
if(!$inQual){
if(/^(>|@)((.+?)( .*?\s*)?)$/){
my $newSeqID = $2;
my $newShortID = $3;
if($seqID){
printf("%d %s\n", length($seq), $seqID);
push(@lengths, length($seq));
}
...
我可以看到IO::Uncompress::Gunzip
通过以下方式支持透明解压缩:
如果设置了此选项且输入文件/缓冲区不是压缩数据,则模块将允许读取它。
此外,如果输入文件/缓冲区确实包含压缩数据并且紧随其后有非压缩数据,则设置此选项将使该模块将整个文件/缓冲区视为单个数据流。
我想基本上将透明解压缩插入the diamond operator,在加载每个文件和从文件输入中读取一行之间。有谁知道我怎么能这样做?
答案 0 :(得分:5)
我经常使用:
die("Usage: prog.pl [file [...]]\n") if @ARGV == 0 && -t STDIN;
push(@ARGV, "-") unless @ARGV;
for my $fn (@ARGV) {
open(FH, $fn =~ /\.gz$/? "gzip -dc $fn |" : $fn =~ /\.bz2$/? "bzip2 -dc $fn |" : $fn) || die;
print while (<FH>);
close(FH);
}
此策略仅适用于gzip
等具有适当文件扩展名的文件和名称文件,但一旦满足这些要求,它就可以同时处理各种文件类型。至于-t STDIN
,请参阅explanation here。
答案 1 :(得分:2)
这也是我长期想做的事情。直到最近我才学会了如何做到这一点。
该方法不需要任何文件命名约定。相反,它会检查the gzip magic number,0x1f8b。它需要读取每个文件的前两个字节作为二进制流(使用一个名为unpack的非常漂亮的函数),并检查字节是否与gzip的幻数相匹配。这似乎对我有用:
$ echo "hi world" | gzip -c > hi_world.gz
$ echo "hi world" > hi_world.txt
$ echo "hi world" | gzip -c > not_a_gz_file
$ perl testgz.pl hi_world.gz hi_world.txt not_a_gz_file
hi_world.gz is gzipped!
hi_world.txt is not gzipped :(
not_a_gz_file is gzipped!
testgz.pl
的内容如下。请原谅我的perl。已经有一段时间......
# testgz.pl
my $GZIP_MAGIC_NUMBER = "1f8b";
my $GZIP_MAGIC_NUMBER_LENGTH = 2; # in bytes
for my $arg (@ARGV){
if(is_gzipped($arg)){
print "$arg is gzipped!\n";
} else{
print "$arg is not gzipped :(\n";
}
}
sub is_gzipped{
my $file_name = shift;
open(my $fh, "<", $file_name)
or die "Can't open < $file_name: $!";
read($fh, $line, $GZIP_MAGIC_NUMBER_LENGTH);
close($fh);
return is_line_gzipped($line);
}
sub is_line_gzipped{
my $line = shift;
my $is_gzipped = 0;
if (length($line) >= $GZIP_MAGIC_NUMBER_LENGTH){
my $magic_number = unpack("H4", $line);
$is_gzipped = 1 if($magic_number == $GZIP_MAGIC_NUMBER);
}
return $is_gzipped
}
在回答问题时,我建议您使用函数is_gzipped
检查要打开的文件,然后根据结果选择方法。
答案 2 :(得分:0)
我认为我最挣扎的是戏弄钻石操作员的不同部分。我在Compress::Zlib
documentation中找到了一些似乎与我想要做的很接近的帮助,除了它试图解压缩所有内容(最后是未压缩文件的垃圾输出):
use strict ;
use warnings ;
use Compress::Zlib ;
# use stdin if no files supplied
@ARGV = '-' unless @ARGV ;
foreach my $file (@ARGV) {
my $buffer ;
my $gz = gzopen($file, "rb")
or die "Cannot open $file: $gzerrno\n" ;
print $buffer while $gz->gzread($buffer) > 0 ;
die "Error reading from $file: $gzerrno" . ($gzerrno+0) . "\n"
if $gzerrno != Z_STREAM_END ;
$gz->gzclose() ;
}
我的修改是改为IO::Uncompress::Gunzip
并让透明的解压缩工作:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Uncompress::Gunzip qw(gunzip $GunzipError);
# use stdin if no files supplied
@ARGV = '-' unless @ARGV
foreach my $file (@ARGV) {
my $z = new IO::Uncompress::Gunzip($file, "transparent", 1)
or die "gunzip failed: $GunzipError\n";
while(<$z>){
print;
}
close($z);
}
这似乎只适用于读取和写入文件(例如zcat),但我还没有在我的脚本中测试它。