Python 等效于管道 zcat 结果到 Perl 中的文件句柄

标签 python perl pipe filehandle zcat

我有一个用 Python 编写的巨大管道,它使用非常大的 .gz 文件(压缩后约 14GB),但需要一种更好的方法来将某些行发送到外部软件(formatdb from blast-legacy/2.2.26)。我有一个很久以前有人为我写的 Perl 脚本,它可以非常快地执行此操作,但我需要在 Python 中做同样的事情,因为管道的其余部分是用 Python 编写的,我必须保持这种方式。 Perl 脚本使用两个文件句柄,一个用于保存 .gz 文件的 zcat,另一个用于存储软件需要的行(每 4 行中的 2 行)并将其用作输入。它涉及生物信息学,但不需要经验。文件是fastq格式,软件需要是fasta格式。每 4 行是一个 fastq 记录,取第 1 行和第 3 行并在第 1 行的开头添加“>”,这是 formatdb 软件将用于每条记录的 fasta 等价物。

perl脚本如下:

#!/usr/bin/perl 
my $SRG = $ARGV[0]; # reads.fastq.gz

open($fh, sprintf("zcat %s |", $SRG)) or die "Broken gunzip $!\n";

# -i: input -n: db name -p: program 
open ($fh2, "| formatdb -i stdin -n $SRG -p F") or die "no piping formatdb!, $!\n";

#Fastq => Fasta sub
my $localcounter = 0;
while (my $line = <$fh>){
        if ($. % 4==1){
                print $fh2 "\>" . substr($line, 1);
                $localcounter++;
        }
        elsif ($localcounter == 1){
                print $fh2 "$line";
                $localcounter = 0;
        }
        else{
        }
}
close $fh;
close $fh2;
exit;

效果非常好。我怎么能在 Python 中做同样的事情呢?我喜欢 Perl 如何使用这些文件句柄,但我不确定如何在不创建实际文件的情况下在 Python 中执行此操作。我所能想到的就是 gzip.open 文件并将我需要的每条记录的两行写入一个新文件并将其与“formatdb”一起使用,但它太慢了。有任何想法吗?我需要将它处理到 python 管道中,所以我不能只依赖 perl 脚本,我也想知道一般情况下如何做到这一点。我假设我需要使用某种形式的子流程模块。

这是我的 Python 代码,但它又是变慢的方式,速度是这里的问题(巨大的文件):

#!/usr/bin/env python

import gzip
from Bio import SeqIO # can recognize fasta/fastq records
import subprocess as sp
import os,sys

filename = sys.argv[1] # reads.fastq.gz

tempFile = filename + ".temp.fasta"

outFile = open(tempFile, "w")

handle = gzip.open(filename, "r")
# parses each fastq record
# r.id and r.seq are the 1st and 3rd lines of each record
for r in SeqIO.parse(handle, "fastq"):
    outFile.write(">" + str(r.id) + "\n")
    outFile.write(str(r.seq) + "\n")

handle.close()
outFile.close()

    cmd = 'formatdb -i ' + str(tempFile) + ' -n ' + filename + ' -p F '
    sp.call(cmd, shell=True)

    cmd = 'rm ' + tempFile
    sp.call(cmd, shell=True)

最佳答案

首先,在 Perl 和 Python 中都有一个更好的解决方案:只需使用 gzip 库。在 Python 中,有一个 in the stdlib ;在 Perl 中,您可以在 CPAN 上找到一个。例如:

with gzip.open(path, 'r', encoding='utf-8') as f:
    for line in f:
        do_stuff(line)

zcat 更简单、更高效、更便携。


但是如果你真的想在 Python 中启动一个子进程并控制它的管道,你可以使用 subprocess 来完成。模块。而且,与 perl 不同的是,Python 无需在中间插入一个 shell 就可以做到这一点。在 Replacing Older Functions with the subprocess Module 上的文档中甚至有一个很好的部分给你食谱。

所以:

zcat = subprocess.Popen(['zcat', path], stdout=subprocess.PIPE)

现在,zcat.stdout 是一个类似文件的对象,具有通常的read 方法等,将管道包装到 zcat 子流程。

因此,例如,要在 Python 3.x 中一次读取一个 8K 的二进制文件:

zcat = subprocess.Popen(['zcat', path], stdout=subprocess.PIPE)
for chunk in iter(functools.partial(zcat.stdout.read, 8192), b''):
    do_stuff(chunk)
zcat.wait()

(如果您想在 Python 2.x 中执行此操作,或者一次读取一个文本文件而不是一次读取一个 8K 的二进制文件,或者其他任何更改,则更改与它们相同任何其他文件处理编码。)

关于Python 等效于管道 zcat 结果到 Perl 中的文件句柄,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30062482/

相关文章:

Python "push server"TCP 客户端

python - 如何舍入到 Python 中的特定值

python - 集合运算的奇怪表现

c - 如何读取子进程的输出?

C 双向管不工作

python - 打印列表中给定字典键的所有值

perl - 如何覆盖 Perl 的 open() 函数但使用相同的文件句柄进行测试?

perl - DateTime::Format::CLDR 解析长捷克日期时出现问题

perl - Perl的新功能,大括号/语法问题

c - 在子进程中运行的程序不会循环