繁体   English   中英

使用管道将管道文件输出到 Perl 中的 gzip 的 Python 等效项

[英]Python equivalent of piping file output to gzip in Perl using a pipe

我需要弄清楚如何在Python中将文件输出写入压缩文件,类似于下面的两行:

open ZIPPED, "| gzip -c > zipped.gz";
print ZIPPED "Hello world\n";

在 Perl 中,这使用 Unix gzip 将您打印到 ZIPPED 文件句柄的任何内容压缩到文件“zipped.gz”。

我知道如何使用“import gzip”在 Python 中执行此操作,如下所示:

import gzip
zipped = gzip.open("zipped.gz", 'wb')
zipped.write("Hello world\n")

然而,这是极其缓慢的。 根据分析器,使用该方法占用了我 90% 的运行时间,因为我将 200GB 的未压缩数据写入各种输出文件。 我知道文件系统可能是这里问题的一部分,但我想通过使用 Unix/Linux 压缩来排除它。 这部分是因为我听说使用同一个模块解压也很慢。

ChristopheD 使用subprocess 模块的建议是对这个问题的恰当回答。 但是,我不清楚它是否会解决您的性能问题。 您必须测量新代码的性能才能确定。

要转换您的示例代码:

import subprocess

p = subprocess.Popen("gzip -c > zipped.gz", shell=True, stdin=subprocess.PIPE)
p.communicate("Hello World\n")

由于需要向子进程发送大量数据,因此应该考虑使用 Popen 对象的stdin属性。 例如:

import subprocess

p = subprocess.Popen("gzip -c > zipped.gz", shell=True, stdin=subprocess.PIPE)
p.stdin.write("Some data")

# Write more data here...

p.communicate() # Finish writing data and wait for subprocess to finish

您可能还会发现此问题中的讨论很有帮助。

尝试这样的事情:

from subprocess import Popen, PIPE
f = open('zipped.gz', 'w')
pipe = Popen('gzip', stdin=PIPE, stdout=f)
pipe.communicate('Hello world\n')
f.close()

使用gzip 模块是官方的单一方法,任何其他纯 python 方法不太可能会更快。 尤其如此,因为数据的大小排除了内存选项。 最有可能的,最快的方法是将完整文件写入磁盘并使用进程对该文件调用gz

确保在比较速度时使用相同的压缩级别。 默认情况下,linux gzip 使用级别 6,而 python 使用级别 9。我在 Python 3.6.8 中使用 gzip 版本 1.5 对此进行了测试,压缩了来自 MySQL 转储的 600MB 数据。 使用默认设置:

python 模块需要 9.24 秒并生成一个 47.1 MB 的文件
subprocess gzip 需要 8.61 秒并生成一个 48.5 MB 的文件

将其更改为第 6 级后,它们匹配:
python 模块需要 8.09 秒并生成一个 48.6 MB 的文件
subprocess gzip 需要 8.55 秒并生成一个 48.5 MB 的文件

# subprocess method
start = time.time()
with open(outfile, 'wb') as f:
    subprocess.run(['gzip'], input=dump, stdout=f, check=True)
print('subprocess finished after {:.2f} seconds'.format(time.time() - start))

# gzip method
start = time.time()
with gzip.open(outfile2, 'wb', compresslevel=6) as z:
    z.write(dump)
print('gzip module finished after {:.2f} seconds'.format(time.time() - start))

除了@srgerg的答案,我想通过禁用 shell 选项shell=False来应用相同的方法,这也是在@Moishe Lettvin 的答案中完成的,并在( https://stackoverflow.com/a/3172488/2402577 )上推荐。

import subprocess
def zip():
    f = open("zipped.gz", "w")
    p1 = subprocess.Popen(["echo", "Hello World"], stdout=subprocess.PIPE)
    p2 = subprocess.Popen(["gzip", "-9c"], stdin=p1.stdout, stdout=f)
    p1.stdout.close()
    p2.communicate()
    f.close()

请注意,最初我将此p1 s 输出用于git diff为:

p1 = subprocess.Popen(["git", "diff"], stdout=subprocess.PIPE)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM