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