2017-06-10 90 views
3

我已經編寫了幾個用於處理FASTA/FASTQ文件的腳本(例如fastx-length.pl),但希望使它們更通用,並且同時接受壓縮文件和未壓縮文件作爲命令行參數和標準輸入(以便腳本「只是工作「,當你扔在他們的隨機文件)。對於我來說,處理未壓縮和壓縮的文件(例如壓縮讀取文件,未壓縮的組裝基因組)是很常見的,並且像<(zcat file.fastq.gz)這樣的插槽會很快變得煩人。如何從perl中的stdin和文件中進行透明的gzip解壓縮?

下面是來自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之間,加載每個文件和從文件輸入讀取一行。有誰知道我該怎麼做?

+1

你爲什麼要撥打chomp兩次?是不是第二chomp冗餘? – winni2k

+0

這是爲了在Linux系統上使用Windows格式文件(我常常這樣做,因爲大多數與Windows電腦合作的人)刪除CR/LF。見[這裏](http://www.perlmonks.org/?node_id=830687)。 – gringer

+0

哈哈。我應該真的走出去...... – winni2k

回答

0

我認爲我最掙扎的是琢磨鑽石操作員的不同位。我發現,在Compress::Zlibdocumentation似乎接近一些幫助了我想做的事,但它試圖解壓一切(與生活垃圾產生量爲無壓縮文件結束了):

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)工作,但我還沒有測試它在我的腳本。

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

+0

這是否也適用於Windows?我本來期望Windows沒有gzip和bzip2這樣的奢侈品。 – gringer

+0

確實。測試是一個非常好的主意。 – gringer

+0

謝謝。採取的點。 – user172818

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打開,然後選擇基於結果的方法。

+0

不幸的是,我不能認爲文件可以重新打開。提供的「文件」可能是來自數據流的數據,因此讀取任何字節以檢測文件中的幻數將需要存儲在採樣緩衝區中。 – gringer

+0

沒錯。如果你能保存初始緩衝區,那麼你應該能夠查看前兩個字節並將它們提供給'is_line_gzipped'。但是,如果我沒有弄錯,你仍然有像open2那樣解壓流的問題? – winni2k