使用Python解析大型(20GB)文本文件 - 将2行作为1行读取

15

我正在解析一个20Gb的文件,并将符合特定条件的行输出到另一个文件中,但是偶尔Python会一次读入两行并将它们连接在一起。

inputFileHandle = open(inputFileName, 'r')

row = 0

for line in inputFileHandle:
    row =  row + 1
    if line_meets_condition:
        outputFileHandle.write(line)
    else:
        lstIgnoredRows.append(row)

我已经检查了源代码文件中的行尾,并且它们都是换行符(ASCII字符10)。将问题行单独提取出来进行解析可以正常工作。我是否遇到了一些 Python 的限制?第一个异常行在文件中的位置大约在 4GB 标记附近。


第一个异常是否总是在相同的行数处一致发生?此外,lstIgnoredRows是一个列表,它会增长到多大?如果您只是将感兴趣的行保存到输出文件中,并没有处理您想要忽略的行,那么会发生什么呢? - Levon
1
也许你可以尝试使用类似于这个问题的惰性方法,每次读取文件的较小块?试试这个:https://dev59.com/KnRB5IYBdhLWcg3wxZ7Y - prrao
它每次发生在相同的行数计数。lstIgnoredRows 可能会增长到几千个项目。 - James
1
旁注:当你有20GB的数据时,将字符串添加到lstIgnoredRows可能会变得棘手。为什么不将被忽略的行号写入另一个文件呢? - Hooked
@Hooked .. 是的,我的想法也是,我担心可能会有一个巨大的列表大小(和内存消耗)的问题。 - Levon
2个回答

23

通过快速的谷歌搜索"Python读取大于4GB的文件"可以获得很多结果。这里有一个例子和另外一个进一步说明的例子

这是一个Python的bug。

现在,解释一下这个bug。它不容易被复现,因为它依赖于内部文件缓冲区的大小和传递给fread()的字符数。在Microsoft CRT源代码中,open.c文件中有一个块,以这个令人鼓舞的评论开头:“这是困难的部分。我们在缓冲区末尾找到了一个CR(回车符),我们必须向前看以确定下一个字符是否是LF(换行符)。”奇怪的是,Perl源代码中几乎有一个完全相同的函数副本:http://perl5.git.perl.org/perl.git/blob/4342f4d6df6a7dfa22a470aa21e54a5622c009f3:/win32/win32.c#l3668问题出在调用SetFilePointer()时,它用于在向前看后退一步。但是它将失败,因为无法用32位DWORD返回当前位置。[修复很容易; 你看到了吗?]此时,函数认为下一个read()将返回LF,但实际上不会,因为文件指针没有被向后移动。

解决办法:

但需要注意的是Python 3.x不受影响(原始文件始终以二进制模式打开,并由Python执行CRLF换行符转换)。对于2.7版本,您可以使用io.open()。


1
以二进制模式打开文件解决了这个问题。感谢您的帮助。 - James
以二进制模式打开解决了我的问题,真是救命恩人! - tlamadon

7

4GB标记非常接近可以存储在32位寄存器(2 ** 32)中的最大值。

你发布的代码本身看起来很好,所以我会怀疑你的Python构建中存在错误。

顺便说一下,如果使用enumerate,代码片段会更简洁:

inputFileHandle = open(inputFileName, 'r')

for row, line in enumerate(inputFileHandle):
    if line_meets_condition:
        outputFileHandle.write(line)
    else:
        lstIgnoredRows.append(row)

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