[英]Python UTF-16 CSV reader
我有一个UTF-16 CSV文件,我必须阅读。 Python csv模块似乎不支持UTF-16。
我正在使用python 2.7.2。 我需要解析的CSV文件很大,要运行几个GB的数据。
回答以下约翰·马钦(John Machin)的问题
print repr(open('test.csv', 'rb').read(100))
使用仅包含abc作为内容的test.csv输出
'\xff\xfea\x00b\x00c\x00'
我认为csv文件是在美国Windows机器上创建的。 我正在使用Mac OSX Lion。
如果我使用phihag和test.csv提供的包含一条记录的代码。
使用的示例test.csv内容。 下面是print repr(open('test.csv','rb')。read(1000))输出
'\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'
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 f\xc3\xbcr e \xc2\x96 m \xc2\x85']
['', '', 'I']
预期输出为
['1', '2', 'G', 'S', 'H f\xc3\xbcr e \xc2\x96 m \xc2\x85','','I']
目前,csv模块不支持UTF-16。
在Python 3.x中,csv需要一个文本模式文件,您可以简单地使用open
的encoding参数来强制进行另一种编码:
# 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之外没有任何Unicode字符的极有可能的情况下,您可以利用以下事实: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
打开它。 请解释。
<85>被视为记录的结尾有点担心。 看起来0x85
被识别为NEL(C1控制代码,NEWLINE)。 很有可能该数据最初是以某种传统的单字节编码进行编码的,其中0x85具有含义,但在错误的假设下将其转码为UTF-16,即原始编码为ISO-8859-1,也就是latin1。 文件起源于哪里? IBM大型机? Windows / Unix /经典Mac? 什么国家,地区,语言? 您显然认为<85>并不是换行符; 您认为这意味着什么?
请随时将缩减文件的副本(包括一些<85>的内容)发送sjmachin at lexicon dot net
根据提供的1行样本数据进行更新 。
这证实了我的怀疑。 阅读此 。 引述如下:
...除了在特定平台(例如OpenVMS)上,很少直接使用C1控制字符...。 当它们出现在表面上以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
打开文件codecs.open
import codecs, csv
stream = codecs.open(<yourfile.csv>, encoding="utf-16")
reader = csv.reader(stream)
并使用unicode字符串遍历程序,因为在处理文本时仍应这样做
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.