Python - 如何在没有MemoryError的情况下gzip大文本文件?

 丘圆圆611 发布于 2022-12-10 13:38

我使用以下简单的Python脚本来压缩EC2 m3.large实例上的大文本文件(例如,10GB).但是,我总是得到一个MemoryError:

import gzip

with open('test_large.csv', 'rb') as f_in:
    with gzip.open('test_out.csv.gz', 'wb') as f_out:
        f_out.writelines(f_in)
        # or the following:
        # for line in f_in:
        #     f_out.write(line)

我得到的追溯是:

Traceback (most recent call last):
  File "test.py", line 8, in 
    f_out.writelines(f_in)
MemoryError

我已经阅读了关于这个问题的一些讨论,但仍然不太清楚如何处理这个问题.有人能给我一个关于如何处理这个问题的更容易理解的答案吗?

2 个回答
  • 这里的问题与gzip没有任何关系,以及从10GB文件中逐行读取而没有新行的所有内容:

    另外需要注意的是,我用来测试Python gzip功能的文件是由fallocate -l 10G bigfile_file生成的.

    这给你一个10GB稀疏文件,完全由0字节组成.意思是没有换行字节.意思是第一行是10GB长.这意味着读取第一行需要10GB.(或者甚至可能是20或40GB,如果您使用3.3之前的Python并尝试将其作为Unicode读取.)

    如果要复制二进制数据,请不要逐行复制.无论是普通文件,还是动态GzipFile解压缩,a socket.makefile()还是其他任何东西,你都会遇到同样的问题.

    解决方案是按块复制块.或者只是使用copyfileobj,这会自动为您完成.

    import gzip
    import shutil
    
    with open('test_large.csv', 'rb') as f_in:
        with gzip.open('test_out.csv.gz', 'wb') as f_out:
            shutil.copyfileobj(f_in, f_out)
    

    默认情况下,copyfileobj使用优化的块大小通常非常好并且从不非常糟糕.在这种情况下,您可能实际上需要更小的尺寸或更大的尺寸; 很难预测哪个是先验的.*所以,通过使用timeit不同的bufsize参数(比如,从1KB到8MB的4的幂)来测试它copyfileobj.但默认的16KB可能会足够好,除非你做了很多这样的事情.

    *如果缓冲区大小太大,您可能最终会交替长时间的I/O块和长时间的处理块.如果它太小,您可能最终需要多次读取才能填充单个gzip块.

    2022-12-11 02:06 回答
  • 那很奇怪.如果你试图压缩一个不包含很多换行符的大型二进制文件,我会期待这个错误,因为这样的文件可能包含一个对你的RAM来说太大的"行",但它不应该在一行上发生 - 结构化.csv文件.

    但无论如何,逐行压缩文件效率不高.即使操作系统缓冲磁盘I/O ,读取和写入较大的数据块通常快得多,例如64 kB.

    我在这台机器上有2GB的RAM,我刚刚成功使用下面的程序来压缩2.8GB的tar存档.

    #! /usr/bin/env python
    
    import gzip
    import sys
    
    blocksize = 1 << 16     #64kB
    
    def gzipfile(iname, oname, level):
        with open(iname, 'rb') as f_in:
            f_out = gzip.open(oname, 'wb', level)
            while True:
                block = f_in.read(blocksize)
                if block == '':
                    break
                f_out.write(block)
            f_out.close()
        return
    
    
    def main():
        if len(sys.argv) < 3:
            print "gzip compress in_file to out_file"
            print "Usage:\n%s in_file out_file [compression_level]" % sys.argv[0]
            exit(1)
    
        iname = sys.argv[1]
        oname = sys.argv[2]
        level = int(sys.argv[3]) if len(sys.argv) > 3 else 6
    
        gzipfile(iname, oname, level)
    
    
    if __name__ == '__main__':  
        main()
    

    我正在运行Python 2.6.6并且gzip.open()不支持with.


    正如Andrew Bay在评论中指出的那样,if block == '':在Python 3中无法正常工作,因为block包含字节而不是字符串,并且空字节对象不会比较为空文本字符串.我们可以检查块长度,或者比较b''(它也可以在Python 2.6+中工作),但简单的方法是if not block:.

    2022-12-11 02:13 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有