2011-03-30 8 views
9

Quiero leer algunos archivos bastante grandes (para ser precisos: el conjunto de datos de palabras de google ngram 1) y contar cuántas veces ocurre un personaje. Ahora escribí este script:Python lee un archivo enorme línea por línea con codificación UFT-8

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) 

que funciona bien, hasta que alcanza aprox. línea de 700.000 del primer archivo, a continuación, obtener este error:

../../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> 

Para resolver este He buscado en la web un poco, y se acercó con este código:

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) 

pero el gancho ahora uso intenta leer todo el archivo de 990 MB en la memoria a la vez, lo que bloquea mi PC. ¿Alguien sabe cómo reescribir este código para que realmente funcione?

p.s: el código ni siquiera ha corrido hasta el final, por lo que ni siquiera sé si hace lo que tiene que hacer, pero para que eso suceda, primero tengo que solucionar este error.

Ah, y yo uso de Python 3.2

+0

comparación de rendimiento de contar caracteres en Python, Cython, C, .. http://stackoverflow.com/questions/2522152/python-is-a-dictionary-slow-to-find-frequency-of- each-character/2525617 # 2525617 – jfs

+0

Para la gente interesada, el resultado (se agregó un filtro para solo az e hizo todo en minúscula): {'a': 102037493781, 'c': 42883014812, 'b': 19831999435, 'e' : 160625131890, 'd': 49858005683, 'g': 23703400644, 'f': 32139997560, 'i': 97477105220, 'h': 63989934675, 'k': 7050807601, 'j': 2260108213, 'm': 32292575753 , 'l': 52782661506, 'o': 100366604971, 'n': 93886203967, 'q': 1622282068, 'p': 27264105140, 's': 85883631327, 'r': 80049264186, 'u': 35187497669, ' t ': 114609472329,' w ': 21891971718,' v ': 13296202464,' y ': 21467638892,' x ': 3007834707,' z ': 1333102460} – teuneboon

Respuesta

7

No sé por qué la entrada de archivos no funciona como se esperaba.

Le sugiero que utilice la función open en su lugar. El valor de retorno puede repetirse y devolverá líneas, al igual que la entrada de archivo.

El código será entonces algo así como:

for filename in files: 
    print(filename) 
    for filelineno, line in enumerate(open(filename, encoding="utf-8")): 
     line = line.strip() 
     data = line.split('\t') 
     # ... 

Algunos enlaces de documentación: enumerate, open, io.TextIOWrapper (retornos abierto una instancia de TextIOWrapper).

+0

Ese wo rked, gracias, ¿que no funciona con fileinput podría ser un error en Python? – teuneboon

+0

Sí, muy bien podría ser. Parece un poco extraño. También podría ser que una de las opciones que pasa a la entrada de archivos hace que se comporte de esta manera. No sé lo suficiente sobre la información de archivos para saber. – codeape

+0

@teuneboon: Parece que su archivo es ASCII correcto. Esta es una suposición predeterminada en muchos paquetes de Python. –

0

Podría tratar de leer no es un archivo completo, pero una parte de ella como binarios, entonces decodificar(), y luego proccess, a continuación, llamar de nuevo la función de leer otra parte ?

0

yo no, si el que tengo es la versión más reciente (y no me acuerdo cómo me las leo), pero ...

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

has necesitado o fileinput.hook_encoded('ascii')fileinput.hook_encoded('latin_1')? No estoy seguro de por qué esto marcaría la diferencia, ya que creo que estos son solo subconjuntos de Unicode con el mismo mapeo, pero vale la pena intentarlo.

EDIT Creo que esto podría ser un error en la entrada de archivos, ninguno de estos funcionan.

0

Si está preocupado por el Uso de memoria, ¿por qué no leer a línea con readline()? Esto eliminará los problemas de memoria con los que se está metiendo. Actualmente está leyendo el archivo completo antes de realizar cualquier acción en el archivo Obj, con readline() no está guardando los datos, simplemente buscándolos por línea.

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 

no tuve la oportunidad de ver muy por encima de su código, pero las muestras anteriores se debería dar una idea de cómo hacer los cambios apropiados a su estructura.charCount1() demuestra el método que almacena el archivo entero en una sola llamada de lectura(). Probé tu método en un archivo de texto de +400 MB y el proceso python.exe llegó a + 900 MB. cuando se ejecuta charCount2(), el proceso python.exe no debe exceder más de unos pocos MB de (siempre y cuando no haya granel hasta el tamaño con otro código);)

+0

fyi, _file = "C: \\ your \\ bulky \\ file.name" y _char = el caracter a buscar;) – AWainb

2

El problema es que fileinput no utiliza file.xreadlines(), que lee línea por línea, pero file.readline(bufsize), que lee bytes de bufsize a la vez (y lo convierte en una lista de líneas). Proporciona 0 para el parámetro bufsize de fileinput.input() (que también es el valor predeterminado). Bufsize 0 significa que el archivo completo está almacenado en el búfer.

Solución: proporcione un bufsize razonable.

1

Esto funciona para mí: puede usar "utf-8" en la definición de gancho. Lo usé en un archivo de líneas de 50GB/200M sin ningún problema.

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