读取大文件时跳过长行以避免MemoryError



我需要扫描两个大的txt文件(都大约100GB,10亿行,几列),并取出某一列(写入新文件)。文件看起来像这个

ID*DATE*provider
1111*201101*1234
1234*201402*5678
3214*201003*9012
...

我的Python脚本是

N100 = 10000000   ## 1% of 1 billion rows
with open("myFile.txt") as f:
with open("myFile_c2.txt", "a") as f2:
perc = 0
for ind, line in enumerate(f):   ## <== MemoryError
c0, c1, c2  = line.split("*")
f2.write(c2+"n")
if ind%N100 == 0: 
print(perc, "%")
perc+=1

现在,上面的脚本对一个文件运行良好,但对另一个文件却停留在62%。对于for ind, line in enumerate(f):,错误消息显示MemoryError。我在不同的服务器上用不同的RAM尝试了几次,错误都是一样的,都是62%。我等了几个小时才监控RAM,当62%时,它爆炸式地达到了28GB(总容量=32GB)。所以我想在那个文件中有一行太长了(可能不是以n结尾?),因此Python在尝试将其读取到RAM时卡住了。

所以我的问题是,在我去我的数据提供商之前,我能做些什么来检测错误行,并以某种方式绕过/跳过将其作为一条巨大的行读取?感谢您的建议!

编辑:

从"错误行"开始的文件可能会被另一个行分隔符(而不是n)打乱。如果是这种情况,我可以检测行sep并继续提取我想要的列,而不是丢弃它们吗?谢谢

这个(未经测试的)代码可能会解决您的问题。它将每次读取的输入限制为1000000字节,以减少最大内存消耗。

请注意,此代码从每行返回第一个百万个字符。还有其他的可能性,如何处理一个长队:

  • 返回前一百万个字符
  • 返回最后一百万个字符
  • 完全跳过这一行,可以选择记录,或者
  • 引发异常

#UNTESTED
def read_start_of_line(fp):
n = int(1e6)
tmp = result = fp.readline(n)
while tmp and tmp[-1] != 'n':
tmp = fp.readline(n)
return result
N100 = 10000000   ## 1% of 1 billion rows
with open("myFile.txt") as f:
with open("myFile_c2.txt", "a") as f2:
perc = 0
for ind, line in enumerate(iter(lambda: read_start_of_line(f), '')):
c0, c1, c2  = line.split("*")
f2.write(c2+"n")
if ind%N100 == 0:
print(perc, "%")
perc+=1

指定最大块大小可以解决内存溢出的问题,同时还可以处理整个文件。以下生成器功能应该可以帮助您做到这一点:

def chunks(f, bufsize):
while True:
chunk = f.readline(bufsize)
if not chunk:
break
yield chunk
if chunk[-1] == "n":
break
def lines(path, bufsize):
with open(path) as f:
pos = -1
while f.tell() > pos:
pos = f.tell()
c = chunks(f, bufsize)
yield c
for _ in c:
pass

以下是如何从每行中只读取前20个字符的示例:

import itertools
for i, line in enumerate(lines("./core/scrape.js", 10)):
print(i, end=": ")
print(''.join(itertools.islice(line, 2)).rstrip())

输出看起来像:

0: /**
1:  * Document scraper/
2:  *
3:  * @author Branden H
4:  * @license MIT
5:  *
6:  */
7:
8: var promise = requir
9: var fs = promise.pro
10: var _ = require("lod
11: var util = require("
12: const path = require

最新更新