Python使用utf-8编码逐行读取大文件

时间:2011-03-30 22:11:12

标签: python dataset file-io

我想阅读一些非常庞大的文件(确切地说:谷歌ngram 1字数据集)并计算一个字符出现的次数。现在我写了这个脚本:

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的文件一次性读入内存,这会让我的电脑崩溃。有谁知道如何重写这段代码,以便它真正起作用?

p.s:代码还没有完全运行,所以我甚至都不知道它是否做了它必须做的事情,但为了实现这一点,我首先要解决这个问题。

哦,我使用Python 3.2

6 个答案:

答案 0 :(得分:7)

我不知道为什么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')
        # ...

一些文档链接:enumerateopenio.TextIOWrapper(打开返回TextIOWrapper的实例)。

答案 1 :(得分:2)

问题是fileinput不使用逐行读取的file.xreadlines(),而是file.readline(bufsize),它一次读取bufsize字节(并将其转换为行列表) )。您为0的{​​{1}}参数提供了bufsize(这也是默认值)。 Bufsize 0意味着整个文件被缓冲。

解决方案:提供合理的bufsize。

答案 2 :(得分:1)

这对我有用:你可以在钩子定义中使用“utf-8”。我在50GB / 200M线文件上使用它没有问题。

fi = fileinput.FileInput(openhook=fileinput.hook_encoded("iso-8859-1"))

答案 3 :(得分:0)

你能不能尝试读取整个文件,但是它的一部分是二进制文件,然后是decode(),然后是proccess,然后再次调用该函数来读取另一部分?

答案 4 :(得分:0)

如果我拥有的是最新版本(我不记得我是如何阅读的),我不会这样做,但是......

$ file -i googlebooks-eng-1M-1gram-20090715-0.csv 
googlebooks-eng-1M-1gram-20090715-0.csv: text/plain; charset=us-ascii

您是否尝试过fileinput.hook_encoded('ascii')fileinput.hook_encoded('latin_1')?不知道为什么这会产生影响,因为我认为这些只是具有相同映射的unicode子集,但值得一试。

编辑我认为这可能是fileinput中的错误,这些都不起作用。

答案 5 :(得分:0)

如果您担心内存使用情况,为什么不使用 readline() 逐行阅读?这将消除您遇到的内存问题。目前,您在对fileObj执行任何操作之前正在阅读完整文件, readline() 您没有保存数据,只是按行进行搜索。

def charCount1(_file, _char):
  result = []
  file   = open(_file, encoding="utf-8")
  data   = file.read()
  file.close()
  for index, line in enumerate(data.split("\n")):
    if _char in line:
      result.append(index)
  return result

def charCount2(_file, _char):
  result = []
  count  = 0
  file   = open(_file, encoding="utf-8")
  while 1:
    line = file.readline()
    if _char in line:
      result.append(count)
    count += 1
    if not line: break
  file.close()
  return result

我没有机会真正查看您的代码,但上面的示例应该让您了解如何对您的结构进行适当的更改。 charCount1() 演示了通过 read() 在一次调用中缓存整个文件的方法。我在+ 400MB文本文件上测试了你的方法,python.exe进程高达+ 900MB。当您运行 charCount2() 时,python.exe进程不应超过几MB(如果您没有使用其他代码扩大其大小); )