我強烈建議你重新編碼爲UTF-8你的文件。在很可能的條件下,您沒有任何Unicode字符以外的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重新編碼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控制代碼,NEWLINE)。原始數據最初是用一些傳統的單字節編碼編碼的,其中0x85具有含義,但在假設原始編碼是ISO-8859-1又名latin1的情況下已被轉碼爲UTF-16。文件來自哪裏?一臺IBM大型機? Windows/Unix /經典Mac?什麼國家,地區,語言?你顯然認爲這並不意味着是一個換行符;你認爲這意味着什麼?
請隨時切下文件的副本(包括一些< 85>的東西)的基礎上提供了1行樣本數據發送到sjmachin at lexicon dot net
更新。
這證實了我的懷疑。閱讀this。下面是它報價:
... the C1 control characters ... are rarely used directly, except on specific platforms such as OpenVMS. When they turn up in documents, Web pages, e-mail messages, etc., which are ostensibly in an ISO-8859-n encoding, their code positions generally refer instead to the characters at that position in a proprietary, system-specific encoding such as Windows-1252 or the Apple Macintosh ("MacRoman") character set that use the codes provided for representation of the C1 set with a single 8-bit byte to instead provide additional graphic characters
此代碼:
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即受保護區域的開始(block-ori使用)
或
EN DASH
?
看起來像一個更大的數據樣本的徹底分析是有保證的。樂於幫助。
感謝@phihag的回覆。有沒有辦法做到這一點,而無需將文件加載到內存中?我的csv文件很大。 – venky 2012-02-07 14:53:27
@venky更新了應該在2.x中工作的黑客。 – phihag 2012-02-07 15:02:45
如何知道文件是否以BOM開頭?@phihag – venky 2012-02-07 15:15:32