使用Python逐行读取大型压缩文本文件

35

我正在尝试使用zipfile模块从归档文件中读取文件。未压缩的文件大小约为3GB,而压缩文件大小为200MB。由于我需要逐行处理压缩文件,因此不想将它们都加载到内存中。目前,使用以下代码时注意到了内存过度使用:

import zipfile
f = open(...)
z = zipfile.ZipFile(f)
for line in zipfile.open(...).readlines()
  print line

我使用SharpZipLib在C#中完成了这个操作:

var fStream = File.OpenRead("...");
var unzipper = new ICSharpCode.SharpZipLib.Zip.ZipFile(fStream);
var dataStream =  unzipper.GetInputStream(0);

dataStream未经压缩,我似乎找不到在Python中完成此操作的方法。希望能得到帮助。

2个回答

80

Python文件对象提供迭代器,可以逐行读取。 file.readlines() 会一次性读取所有行并返回一个列表 - 这意味着它需要将所有内容读入内存中。更好的方法(始终优先于readlines())是直接循环遍历对象本身,例如:

import zipfile
with zipfile.ZipFile(...) as z:
    with z.open(...) as f:
        for line in f:
            print line

请注意我使用了with语句 - 文件对象是上下文管理器,而with语句使我们能够轻松编写可读代码,确保在退出块时关闭文件(即使出现异常)。再次强调,在处理文件时应始终使用此方法。


无法说得更好 - Loïc Faure-Lacroix
@Gareth Latty,有没有关于open函数接受哪种类型参数的解释文档?我想看看是否可以像“with open()”函数一样为open()设置内存缓冲区。 - edo101
1
我注意到的另一件事是,z.open() 似乎不允许使用 r 选项。当你需要在 for line in f: 块中运行一些逻辑时,这就会发挥作用。例如: if line.find("YES") != -1: print('yay')。这会返回一个 TypeError。你必须在 "YES" 前面加上一个 b 才能使它工作。 - ericOnline
1
@ericOnline 这是因为你得到的是字节,而不是Unicode字符串。根据使用情况,你可能想要做一些像解码为UTF-8这样的操作,以获取真正的字符串,而不仅仅是使用字节字符串。 - Gareth Latty
好的。我在找不到在函数中放置.decode()的位置。还是我要将其包装在函数调用周围?我会尝试实验一下。 - ericOnline
2
使用io.TextIOWrapper;例如with io.TextIOWrapper(z.open(...), encoding='utf-8') as f: - Ali Akbar

0
如果压缩文件中的内部目录和子目录文件名不重要,您可以尝试以下方法:
from zipfile import ZipFile
from io import TextIOWrapper

def zip_open(filename):
    """Wrapper function that for zipfiles."""
    with ZipFile(filename) as zipfin:
        for filename in zipfin.namelist():
            return TextIOWrapper(zipfin.open(filename))

# Usage of the zip_open function)
with zip_open('myzipball.zip') as fin:
    for line in fin:
        print(line)

zip_open 在 zip 文件中包含单个或多个文件且没有子目录时运行良好。但是,如果压缩文件中存在复杂的子目录结构,则不确定简单的 for filename in zipfin.namelist() 是否有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接