【发布时间】:2016-07-01 13:08:44
【问题描述】:
我正在处理一些相当大的 gzip 压缩文本文件,我必须对其进行解压缩、编辑和重新压缩。我使用 Python 的 gzip 模块进行解压和压缩,但我发现我目前的实现远非最佳:
input_file = gzip.open(input_file_name, 'rb')
output_file = gzip.open(output_file_name, 'wb')
for line in input_file:
# Edit line and write to output_file
这种方法慢得让人难以忍受——可能是因为使用 gzip 模块进行每行迭代会产生巨大的开销:我最初还运行了一个行计数例程,其中我使用 gzip 模块读取文件的块并然后计算每个块中换行符的数量,这非常快!
因此,其中一项优化绝对应该是以块的形式读取我的文件,然后仅在解压缩块后才进行每行迭代。
作为额外的优化,我看到了一些通过子进程在 shell 命令中解压缩的建议。使用这种方法,上面第一行的等价物可能是:
from subprocess import Popen, PIPE
file_input = Popen(["zcat", fastq_filename], stdout=PIPE)
input_file = file_input.stdout
使用这种方法 input_file 变成了一个类似文件的对象。我不知道它在可用属性和方法方面与真实文件对象有何不同,但一个区别是您显然不能使用 seek,因为它是流而不是文件。
这确实运行得更快,而且应该 - 除非您在声称的单核机器上运行您的脚本。后者一定意味着子进程会尽可能自动将不同的线程发送到不同的内核,但我不是那里的专家。
现在解决我当前的问题:我想以类似的方式压缩我的输出。也就是说,我不想使用 Python 的 gzip 模块,而是将其通过管道传输到子进程,然后调用 shell gzip。这样我就有可能在不同的内核中进行阅读、编辑和写作,这对我来说听起来非常有效。 我对此进行了微不足道的尝试,但尝试写入 output_file 会导致一个空文件。最初,我使用 touch 命令创建了一个空文件,因为如果文件不存在,Popen 会失败:
call('touch ' + output_file_name, shell=True)
output = Popen(["gzip", output_file_name], stdin=PIPE)
output_file = output.stdin
非常感谢任何帮助,顺便说一下,我使用的是 Python 2.7。谢谢。
【问题讨论】:
-
如果您有性能问题,然后创建一个可用作基准的最小代码示例并说明所需的目标(例如,使其处理(gzipped)数据 100MB/s -- 我怀疑你的磁盘比这快得多——处理(压缩)数据的速度比你的磁盘读/写的速度更快)——并将其作为一个单独的问题发布。
-
不是真的 - 只是提供一些背景,以防我处理这一切都错了。我真正想知道的是如何通过管道传输到 shell 程序,在我的情况下最终得到一个不错的 gzip 文件。读/写速度不是这里的问题。如果输入和输出文件是纯文本文件,即具有更多数据要读/写的更大文件,它运行得更快。总体而言,在 shell 中解压缩输入文件、运行脚本和在 shell 中压缩输出文件也更快。
标签: python bash shell subprocess popen