Python читает огромный файл построчно с кодировкой utf-8 - PullRequest
9 голосов
/ 31 марта 2011

Я хочу прочитать несколько довольно больших файлов (если быть точным: набор данных Google ngram 1 word) и посчитать, сколько раз встречается символ.Теперь я написал этот скрипт:

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)

, ноловушка, которую я сейчас использую, пытается прочитать весь файл размером 990 МБ в память сразу, что приводит к сбою моего компьютера.Кто-нибудь знает, как переписать этот код, чтобы он действительно работал?

ps: код еще даже не запускался полностью, поэтому я даже не знаю, выполняет ли он то, что должен,но чтобы это произошло, мне сначала нужно исправить эту ошибку.

О, и я использую Python 3.2

Ответы [ 6 ]

7 голосов
/ 31 марта 2011

Я не знаю, почему 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')
        # ...

Некоторые ссылки на документацию: перечисление , open , io.TextIOWrapper (open возвращает экземпляр TextIOWrapper).

2 голосов
/ 01 апреля 2011

Проблема в том, что fileinput не использует file.xreadlines(), который читает строку за строкой, а file.readline(bufsize), который сразу читает байты bufsize (и превращает это в список строк).Вы предоставляете 0 для параметра bufsize fileinput.input() (который также является значением по умолчанию).Bufsize 0 означает, что весь файл буферизован.

Решение: предоставить разумный размер bufsize.

1 голос
/ 21 марта 2013

Это работает для меня: вы можете использовать "utf-8" в определении ловушки. Я без проблем использовал его для файла со строками 50 ГБ / 200 МБ.

fi = fileinput.FileInput(openhook=fileinput.hook_encoded("iso-8859-1"))
0 голосов
/ 31 марта 2011

Если вас беспокоит использование mem, почему бы не прочитать строку с помощью 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 () . Я проверил ваш метод на текстовом файле + 400 МБ, а процесс python.exe поднялся до + 900 МБ. когда вы запускаете charCount2 () , процесс python.exe не должен превышать несколько МБ (при условии, что вы не увеличили размер с другим кодом);)

0 голосов
/ 31 марта 2011

Не знаю, последняя ли у меня версия (и я не помню, как я их читал), но ...

$ 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 с тем же отображением, но стоит попробовать.

РЕДАКТИРОВАТЬ Я думаю, что это может быть ошибка в вводе файла, ни одна из этих работ.

0 голосов
/ 31 марта 2011

Не могли бы вы попытаться прочитать не весь файл, а его часть как двоичный файл, затем decode (), затем обработать, а затем снова вызвать функцию, чтобы прочитать другую часть?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...