Volver a codificar la secuencia de Unicode como Ascii ignorando errores

Estoy intentando tomar una secuencia de archivos Unicode, que contiene caracteres impares, y envolverla con un lector de secuencias que la convertirá a Ascii, ignorando o reemplazando a todos los caracteres que no puedan codificarse.

Mi flujo se ve como:

"EventId","Rate","Attribute1","Attribute2","(。・ω・。)ノ" ... 

Mi bash de alterar el flujo sobre la marcha se ve así:

 import chardet, io, codecs with open(self.csv_path, 'rb') as rawdata: detected = chardet.detect(rawdata.read(1000)) detectedEncoding = detected['encoding'] with io.open(self.csv_path, 'r', encoding=detectedEncoding) as csv_file: csv_ascii_stream = codecs.getreader('ascii')(csv_file, errors='ignore') log( csv_ascii_stream.read() ) 

El resultado en la línea de log es: UnicodeEncodeError: 'ascii' codec can't encode characters in position 36-40: ordinal not in range(128) , aunque construí el StreamReader explícitamente con errors='ignore'

Me gustaría que la secuencia resultante (cuando se lea) salga así:

 "EventId","Rate","Attribute1","Attribute2","(?????)?" ... 

o alternativamente, "EventId","Rate","Attribute1","Attribute2","()" (usando 'ignore' lugar de 'replace' )

¿Por qué está sucediendo la excepción de todos modos?

He visto muchos problemas / soluciones para decodificar cadenas, pero mi desafío es cambiar la secuencia a medida que se lee (usando .next() ), porque el archivo es potencialmente demasiado grande para cargarlo en la memoria de una sola vez .read()

Estás mezclando la encoding y la deencoding de los lados.

Para decodificar, lo estás haciendo bien. Lo abre como datos binarios, chardet el primer 1K y chardet en modo de texto utilizando la encoding detectada.

Pero luego estás intentando decodificar aún más los datos ya decodificados como ASCII, usando codecs.getreader . Esa función devuelve un StreamReader , que decodifica datos de un flujo. Eso no va a funcionar. Necesita codificar esos datos a ASCII.

Pero no está claro por qué está utilizando un codificador o codificador de flujo de codecs en primer lugar, cuando todo lo que quiere hacer es codificar una sola porción de texto de una sola vez para que pueda registrarlo. ¿Por qué no llamar simplemente al método de encode ?

 log(csv_file.read().encode('ascii', 'ignore')) 

Si desea algo que pueda usar como un perezoso iterable de líneas, podría construir algo completamente general, pero es mucho más simple simplemente hacer algo como el ejemplo de UTF8Recorder en los documentos csv :

 class AsciiRecoder: def __init__(self, f, encoding): self.reader = codecs.getreader(encoding)(f) def __iter__(self): return self def next(self): return self.reader.next().encode("ascii", "ignore") 

O, aún más simple:

 with io.open(self.csv_path, 'r', encoding=detectedEncoding) as csv_file: csv_ascii_stream = (line.encode('ascii', 'ignore') for line in csv_file) 

Llego un poco tarde a la fiesta con esto, pero aquí hay una solución alternativa, utilizando codecs.StreamRecoder :

 from codecs import getencoder, getdecoder, getreader, getwriter, StreamRecoder with io.open(self.csv_path, 'rb') as f: csv_ascii_stream = StreamRecoder(f, getencoder('ascii'), getdecoder(detectedEncoding), getreader(detectedEncoding), getwriter('ascii'), errors='ignore') print(csv_ascii_stream.read()) 

Supongo que querrá usar esto si necesita la flexibilidad para poder llamar a read() / readlines() / seek() / tell() etc. en la secuencia que se devuelve. Si solo necesita recorrer el flujo, la expresión generadora que proporciona abarnert es un poco más concisa.