python csv utf-16

Lector Python UTF-16 CSV



(4)

El ejemplo de documentación del módulo Python 2.x csv muestra cómo manejar otras codificaciones.

Tengo un archivo CSV UTF-16 que tengo que leer. El módulo Python csv no parece ser compatible con UTF-16.

Estoy usando python 2.7.2. Los archivos CSV que necesito analizar son de gran tamaño y se ejecutan en varios GB de datos.

Respuestas para las preguntas de John Machin a continuación

print repr(open(''test.csv'', ''rb'').read(100))

Salida con test.csv teniendo solo abc como contenido

''/xff/xfea/x00b/x00c/x00''

Creo que el archivo csv se creó en la máquina de Windows en EE. UU. Estoy usando Mac OSX Lion.

Si utilizo el código provisto por phihag y test.csv que contiene un registro.

muestra del contenido de test.csv utilizado. A continuación se muestra print repr (abrir (''test.csv'', ''rb''). Leer (1000)) de salida

''/xff/xfe1/x00,/x002/x00,/x00G/x00,/x00S/x00,/x00H/x00 /x00f/x00/xfc/x00r/x00 /x00e/x00 /x00/x96/x00 /x00m/x00 /x00/x85/x00,/x00,/x00I/x00/r/x00/n/x00''

Código por phihag

import codecs import csv with open(''test.csv'',''rb'') as f: sr = codecs.StreamRecoder(f,codecs.getencoder(''utf-8''),codecs.getdecoder(''utf-8''),codecs.getreader(''utf-16''),codecs.getwriter(''utf-16'')) for row in csv.reader(sr): print row

Salida del código anterior

[''1'', ''2'', ''G'', ''S'', ''H f/xc3/xbcr e /xc2/x96 m /xc2/x85''] ['''', '''', ''I'']

el resultado esperado es

[''1'', ''2'', ''G'', ''S'', ''H f/xc3/xbcr e /xc2/x96 m /xc2/x85'','''',''I'']


Le sugiero encarecidamente que recodifique su archivo (s) a UTF-8. En la condición muy probable de que no tenga ningún carácter Unicode fuera del BMP, puede aprovechar el hecho de que UTF-16 es una codificación de longitud fija para leer bloques de longitud fija desde su archivo de entrada sin preocuparse por el bloqueo a horcajadas límites.

Paso 1: determina qué codificación tienes en realidad. Examine los primeros bytes de su archivo:

print repr(open(''thefile.csv'', ''rb'').read(100))

Cuatro formas posibles de codificar u''abc''

/xfe/xff/x00a/x00b/x00c -> utf_16 /xff/xfea/x00b/x00c/x00 -> utf_16 /x00a/x00b/x00c -> utf_16_be a/x00b/x00c/x00 -> utf_16_le

Si tiene algún problema con este paso, edite su pregunta para incluir los resultados de la print repr() anterior print repr()

Paso 2: Aquí hay una secuencia de comandos de recodo de Python 2.X-UTF-16 * -to-UTF-8:

import sys infname, outfname, enc = sys.argv[1:4] fi = open(infname, ''rb'') fo = open(outfname, ''wb'') BUFSIZ = 64 * 1024 * 1024 first = True while 1: buf = fi.read(BUFSIZ) if not buf: break if first and enc == ''utf_16'': bom = buf[:2] buf = buf[2:] enc = {''/xfe/xff'': ''utf_16_be'', ''/xff/xfe'': ''utf_16_le''}[bom] # KeyError means file doesn''t start with a valid BOM first = False fo.write(buf.decode(enc).encode(''utf8'')) fi.close() fo.close()

Otros asuntos:

Usted dice que sus archivos son demasiado grandes para leer todo el archivo, recodificarlos y reescribirlos, pero puede abrirlos en vi . Por favor explique.

El <85> tratado como final de registro es un poco preocupante. Parece que 0x85 se reconoce como NEL (código de control C1, NEWLINE). Existe una gran posibilidad de que los datos estuvieran originalmente codificados en alguna codificación heredada de un byte, donde 0x85 tiene un significado pero ha sido transcodificado a UTF-16 bajo la falsa suposición de que la codificación original era ISO-8859-1 también conocida como latin1. ¿De dónde se originó el archivo? ¿Un mainframe de IBM? Windows / Unix / classic Mac? ¿Qué país, lugar, idioma? Usted obviamente piensa que el <85> no está destinado a ser una nueva línea; ¿Qué piensas que significa?

Por favor, siéntase libre de enviar una copia de un archivo reducido (que incluye algunas de las cosas <85>) a sjmachin at lexicon dot net

Actualización basada en datos de muestra de 1 línea proporcionados.

Esto confirma mis sospechas. Lee esto Aquí hay una cita de esto:

... los caracteres de control C1 ... rara vez se usan directamente, excepto en plataformas específicas como OpenVMS. Cuando aparecen en documentos, páginas web, mensajes de correo electrónico, etc., que están ostensiblemente en una codificación ISO-8859-n, sus posiciones de código generalmente se refieren a los caracteres en esa posición en una codificación patentada específica del sistema como el juego de caracteres Windows-1252 o Apple Macintosh ("MacRoman") que utiliza los códigos provistos para la representación del conjunto C1 con un solo byte de 8 bits para proporcionar caracteres gráficos adicionales en su lugar

Este código:

s1 = ''/xff/xfe1/x00,/x002/x00,/x00G/x00,/x00S/x00,/x00H/x00 /x00f/x00/xfc/x00r/x00 /x00e/x00 /x00/x96/x00 /x00m/x00 /x00/x85/x00,/x00,/x00I/x00/r/x00/n/x00'' s2 = s1.decode(''utf16'') print ''s2 repr:'', repr(s2) from unicodedata import name from collections import Counter non_ascii = Counter(c for c in s2 if c >= u''/x80'') print ''non_ascii:'', non_ascii for c in non_ascii: print "from: U+%04X %s" % (ord(c), name(c, "<no name>")) c2 = c.encode(''latin1'').decode(''cp1252'') print "to: U+%04X %s" % (ord(c2), name(c2, "<no name>")) s3 = u''''.join( c.encode(''latin1'').decode(''1252'') if u''/x80'' <= c < u''/xA0'' else c for c in s2 ) print ''s3 repr:'', repr(s3) print ''s3:'', s3

produce lo siguiente (Python 2.7.2 IDLE, Windows 7):

s2 repr: u''1,2,G,S,H f/xfcr e /x96 m /x85,,I/r/n'' non_ascii: Counter({u''/x85'': 1, u''/xfc'': 1, u''/x96'': 1}) from: U+0085 <no name> to: U+2026 HORIZONTAL ELLIPSIS from: U+00FC LATIN SMALL LETTER U WITH DIAERESIS to: U+00FC LATIN SMALL LETTER U WITH DIAERESIS from: U+0096 <no name> to: U+2013 EN DASH s3 repr: u''1,2,G,S,H f/xfcr e /u2013 m /u2026,,I/r/n'' s3: 1,2,G,S,H für e – m …,,I

¿Cuál crees que es una interpretación más razonable de /x96 ?

SPA, es decir, inicio del área protegida (utilizado por terminales orientados a bloques).
o
EN DASH
?

Parece que se justifica un análisis exhaustivo de una muestra de datos mucho mayor. Feliz de ayudar.


Por el momento, el módulo csv no es compatible con UTF-16.

En Python 3.x, csv espera un archivo en modo texto y simplemente puede usar el parámetro de codificación de open para forzar otra codificación:

# Python 3.x only import csv with open(''utf16.csv'', ''r'', encoding=''utf16'') as csvf: for line in csv.reader(csvf): print(line) # do something with the line

En Python 2.x, puede recodificar la entrada:

# Python 2.x only import codecs import csv class Recoder(object): def __init__(self, stream, decoder, encoder, eol=''/r/n''): self._stream = stream self._decoder = decoder if isinstance(decoder, codecs.IncrementalDecoder) else codecs.getincrementaldecoder(decoder)() self._encoder = encoder if isinstance(encoder, codecs.IncrementalEncoder) else codecs.getincrementalencoder(encoder)() self._buf = '''' self._eol = eol self._reachedEof = False def read(self, size=None): r = self._stream.read(size) raw = self._decoder.decode(r, size is None) return self._encoder.encode(raw) def __iter__(self): return self def __next__(self): if self._reachedEof: raise StopIteration() while True: line,eol,rest = self._buf.partition(self._eol) if eol == self._eol: self._buf = rest return self._encoder.encode(line + eol) raw = self._stream.read(1024) if raw == '''': self._decoder.decode(b'''', True) self._reachedEof = True return self._encoder.encode(self._buf) self._buf += self._decoder.decode(raw) next = __next__ def close(self): return self._stream.close() with open(''test.csv'',''rb'') as f: sr = Recoder(f, ''utf-16'', ''utf-8'') for row in csv.reader(sr): print (row)

open y codecs.open requieren que el archivo comience con una lista de materiales. Si no lo hace (o está en Python 2.x), puede convertirlo en memoria, así:

try: from io import BytesIO except ImportError: # Python < 2.6 from StringIO import StringIO as BytesIO import csv with open(''utf16.csv'', ''rb'') as binf: c = binf.read().decode(''utf-16'').encode(''utf-8'') for line in csv.reader(BytesIO(c)): print(line) # do something with the line


Simplemente abra su archivo con codecs.open como en

import codecs, csv stream = codecs.open(<yourfile.csv>, encoding="utf-16") reader = csv.reader(stream)

Y trabaje a través de su programa con cadenas de caracteres unicode, como debe hacer de todos modos si está procesando texto