我强烈建议您将文件重新编码为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重新编码UTF-16 *至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]
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 aka latin1。文件的来源在哪里?IBM大型机?Windows / Unix / classic Mac?哪个国家,区域设置,语言?您显然认为<85>不是换行符;您认为它代表什么意思?
请随时将包含一些<85>内容的削减文件的副本发送到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 i.e. 保护区域起点(用于块式终端)还是EN DASH?
看起来需要对更大的数据样本进行彻底分析。很乐意帮忙。