簡體   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