Читатель CSV Python UTF-16
у меня есть файл CSV UTF-16, который я должен прочитать. Модуль Python csv, похоже, не поддерживает UTF-16.
Я использую python 2.7.2. CSV-файлы мне нужно парсить огромные размера в несколько Гб данных.
ответы на вопросы John Machin ниже
print repr(open('test.csv', 'rb').read(100))
выход с теста.csv, имеющий только abc в качестве контента
'xffxfeax00bx00cx00'
Я думаю, что csv-файл был создан на машине windows в США. Я использую Mac OSX Lion.
если я использую код предоставлено phihag и test.CSV, содержащий одну запись.
тестовый образец.используется содержимое csv. Ниже приведен print repr (open ('test.csv','rb').читают(1000)) выход
'xffxfe1x00,x002x00,x00Gx00,x00Sx00,x00Hx00 x00fx00xfcx00rx00 x00ex00 x00x96x00 x00mx00 x00x85x00,x00,x00Ix00rx00nx00'
код от 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
вывод вышеуказанного кода
['1', '2', 'G', 'S', 'H fxc3xbcr e xc2x96 m xc2x85']
['', '', 'I']
ожидается выход
['1', '2', 'G', 'S', 'H fxc3xbcr e xc2x96 m xc2x85','','I']
4 ответов
на данный момент модуль csv не поддерживает UTF-16.
В Python 3.x, csv ожидает текстовый файл, и вы можете просто использовать параметр кодирования open
чтобы заставить другую кодировку:
# 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
В Python 2.x, вы можете перекодировать вход:
# 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
и codecs.open
требуются запустите файл с BOM. Если это не так (или вы находитесь на Python 2.x), вы все равно можете преобразовать его в память, например:
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
я настоятельно рекомендую вам перекодировать ваши файлы в UTF-8. При очень вероятном условии, что у вас нет символов Юникода за пределами BMP, вы можете воспользоваться тем фактом, что UTF-16 является кодировкой фиксированной длины для чтения блоков фиксированной длины из вашего входного файла, не беспокоясь о границах блоков.
Шаг 1: определите, какая кодировка у вас на самом деле есть. Изучите первые несколько байтов файл:
print repr(open('thefile.csv', 'rb').read(100))
четыре возможных способа кодирования 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
если у вас возникли проблемы с этим шагом, отредактируйте свой вопрос, чтобы включить результаты выше print repr()
Шаг 2: Вот Python 2.X recode-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()
другие вопросы:
вы говорите, что ваши файлы слишком велики, чтобы прочитать весь файл, перекодировать и переписать, но вы можете открыть его в vi
. Пожалуйста объяснять.
то, что рассматривается как конец записи, немного беспокоит. Похоже на 0x85
распознается как NEL (код управления C1, новая строка). Существует большая вероятность того, что данные были первоначально закодированы в некоторой устаревшей однобайтовой кодировке, где 0x85 имеет значение, но был перекодирован в UTF-16 при ложном предположении, что исходная кодировка была ISO-8859-1 aka latin1. Откуда взялся файл? Мэйнфрейм IBM? Windows / Unix / классический Mac? Какая страна, язык, язык? Вы, очевидно, думаете, что не должно быть новой линией; как вы думаете, что это значит?
пожалуйста, не стесняйтесь отправлять копию сокращенного файла (который включает в себя некоторые вещи) в sjmachin at lexicon dot net
обновление основанный на 1-линии обеспеченных данных по образца.
это подтверждает мои подозрения. Читать этой. Вот цитата из него:
... управляющие символы C1 ... редко используются непосредственно, за исключением конкретные платформы, такие как OpenVMS. Когда они появляются в документах, Веб-страницы, сообщения электронной почты и т. д., которые якобы находятся в Кодировка ISO-8859 - n, их позиции кода обычно ссылаются на характеры на этом положении в собственническом, систем-специфическом кодировка, такая как Windows-1252 или Apple Macintosh ("MacRoman") набор символов, использующий коды, предусмотренные для представления C1 установите с одним 8-битным байтом, чтобы вместо этого предоставить дополнительные графический персонажи
этот код:
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
производит следующее (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
что, по-вашему, является более разумной интерпретацией \x96
:
SPA т. е. начало охраняемой зоны (используется блочно-ориентированными терминалами.)
или
ТИРЕ
?
похоже, что необходим тщательный анализ гораздо большей выборки данных. Рад помочь.
просто откройте файл с помощью codecs.open
как и в
import codecs, csv
stream = codecs.open(<yourfile.csv>, encoding="utf-16")
reader = csv.reader(stream)
и работать через вашу программу со строками unicode, как вы нужно делать в любом случае, если вы обрабатываете текст