Python以utf-8编码逐行读取大文件
我想读取一些非常大的文件(具体来说是谷歌的ngram单词数据集),并计算某个字符出现的次数。于是我写了这个脚本:
import fileinput
files = ['../../datasets/googlebooks-eng-all-1gram-20090715-%i.csv' % value for value in range(0,9)]
charcounts = {}
lastfile = ''
for line in fileinput.input(files):
line = line.strip()
data = line.split('\t')
for character in list(data[0]):
if (not character in charcounts):
charcounts[character] = 0
charcounts[character] += int(data[1])
if (fileinput.filename() is not lastfile):
print(fileinput.filename())
lastfile = fileinput.filename()
if(fileinput.filelineno() % 100000 == 0):
print(fileinput.filelineno())
print(charcounts)
这个脚本运行得很好,直到它处理到第一个文件的大约第700,000行时,我就遇到了这个错误:
../../datasets/googlebooks-eng-all-1gram-20090715-0.csv
100000
200000
300000
400000
500000
600000
700000
Traceback (most recent call last):
File "charactercounter.py", line 5, in <module>
for line in fileinput.input(files):
File "C:\Python31\lib\fileinput.py", line 254, in __next__
line = self.readline()
File "C:\Python31\lib\fileinput.py", line 349, in readline
self._buffer = self._file.readlines(self._bufsize)
File "C:\Python31\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 7771: cha
racter maps to <undefined>
为了修复这个问题,我在网上查了一下,找到了这段代码:
import fileinput
files = ['../../datasets/googlebooks-eng-all-1gram-20090715-%i.csv' % value for value in range(0,9)]
charcounts = {}
lastfile = ''
for line in fileinput.input(files,False,'',0,'r',fileinput.hook_encoded('utf-8')):
line = line.strip()
data = line.split('\t')
for character in list(data[0]):
if (not character in charcounts):
charcounts[character] = 0
charcounts[character] += int(data[1])
if (fileinput.filename() is not lastfile):
print(fileinput.filename())
lastfile = fileinput.filename()
if(fileinput.filelineno() % 100000 == 0):
print(fileinput.filelineno())
print(charcounts)
但是我现在用的这个方法试图一次性把整个990MB的文件都读到内存里,这样我的电脑就崩溃了。有没有人知道怎么改写这段代码,让它真正能工作?
顺便说一下:这段代码还没有完全运行,所以我甚至不知道它是否能完成我想要的功能,但在这之前,我得先解决这个错误。
哦,对了,我使用的是Python 3.2。
6 个回答
1
这个方法对我有效:你可以在钩子定义中使用“utf-8”。我在一个50GB、200万行的文件上使用这个方法,没有遇到任何问题。
fi = fileinput.FileInput(openhook=fileinput.hook_encoded("iso-8859-1"))
2
问题在于,fileinput
并不是逐行读取文件,而是一次性读取一定字节数的内容。具体来说,它使用的是 file.readline(bufsize)
,这个方法会一次读取 bufsize
字节的数据,然后把这些数据变成一行一行的列表。你在调用 fileinput.input()
时,把 bufsize
参数设置为 0
(这也是默认值),这意味着整个文件都会被一次性加载到内存中。
解决办法是:给 bufsize
设置一个合适的值。
8
我不知道为什么fileinput没有按预期工作。
我建议你使用open
这个函数。它的返回值可以被逐行读取,和fileinput的效果一样。
那么代码大概会是这样的:
for filename in files:
print(filename)
for filelineno, line in enumerate(open(filename, encoding="utf-8")):
line = line.strip()
data = line.split('\t')
# ...
这里有一些文档链接:enumerate,open,io.TextIOWrapper(open会返回一个TextIOWrapper的实例)。