我必须编写一个支持读取文件的脚本,该文件可以保存为Unicode或Ansi(使用MS的记事本)。
我对文件中的编码格式没有任何指示,我如何支持这两种编码格式? (一种在不知道高级格式的情况下读取文件的通用方法)。
答案 0 :(得分:11)
MS Notepad为用户提供了4种编码选择,用笨拙的术语表达:
“Unicode”是UTF-16,写成little-endian。 “Unicode big endian”是UTF-16,写的是big-endian。在两种UTF-16情况下,这意味着将写入适当的BOM。使用utf-16
解码此类文件。
“UTF-8”是UTF-8;记事本显式写入“UTF-8 BOM”。使用utf-8-sig
解码此类文件。
“ANSI”令人震惊。这是“无论此计算机上的默认遗留编码是什么”的MS术语。
以下是我所知道的Windows编码列表以及它们用于的语言/脚本:
cp874 Thai
cp932 Japanese
cp936 Unified Chinese (P.R. China, Singapore)
cp949 Korean
cp950 Traditional Chinese (Taiwan, Hong Kong, Macao(?))
cp1250 Central and Eastern Europe
cp1251 Cyrillic ( Belarusian, Bulgarian, Macedonian, Russian, Serbian, Ukrainian)
cp1252 Western European languages
cp1253 Greek
cp1254 Turkish
cp1255 Hebrew
cp1256 Arabic script
cp1257 Baltic languages
cp1258 Vietnamese
cp???? languages/scripts of India
如果在正在读取文件的计算机上创建了文件,则可以通过locale.getpreferredencoding()
获取“ANSI”编码。否则,如果您知道它来自何处,则可以指定在不是UTF-16时使用的编码。没错,猜猜。
小心使用codecs.open()
来读取Windows上的文件。文档说:“”注意
即使未指定二进制模式,文件也始终以二进制模式打开。这样做是为了避免因使用8位值进行编码而导致数据丢失。这意味着在阅读和写作时不会自动转换'\ n'。“”“这意味着您的行将以\r\n
结束,您将需要/想要将其删除。
全部放在一起:
使用所有4种编码选项保存的示例文本文件在记事本中如下所示:
The quick brown fox jumped over the lazy dogs.
àáâãäå
以下是一些演示代码:
import locale
def guess_notepad_encoding(filepath, default_ansi_encoding=None):
with open(filepath, 'rb') as f:
data = f.read(3)
if data[:2] in ('\xff\xfe', '\xfe\xff'):
return 'utf-16'
if data == u''.encode('utf-8-sig'):
return 'utf-8-sig'
# presumably "ANSI"
return default_ansi_encoding or locale.getpreferredencoding()
if __name__ == "__main__":
import sys, glob, codecs
defenc = sys.argv[1]
for fpath in glob.glob(sys.argv[2]):
print
print (fpath, defenc)
with open(fpath, 'rb') as f:
print "raw:", repr(f.read())
enc = guess_notepad_encoding(fpath, defenc)
print "guessed encoding:", enc
with codecs.open(fpath, 'r', enc) as f:
for lino, line in enumerate(f, 1):
print lino, repr(line)
print lino, repr(line.rstrip('\r\n'))
以下是使用命令\python27\python read_notepad.py "" t1-*.txt
('t1-ansi.txt', '')
raw: 'The quick brown fox jumped over the lazy dogs.\r\n\xe0\xe1\xe2\xe3\xe4\xe5
\r\n'
guessed encoding: cp1252
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'
('t1-u8.txt', '')
raw: '\xef\xbb\xbfThe quick brown fox jumped over the lazy dogs.\r\n\xc3\xa0\xc3
\xa1\xc3\xa2\xc3\xa3\xc3\xa4\xc3\xa5\r\n'
guessed encoding: utf-8-sig
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'
('t1-uc.txt', '')
raw: '\xff\xfeT\x00h\x00e\x00 \x00q\x00u\x00i\x00c\x00k\x00 \x00b\x00r\x00o\x00w
\x00n\x00 \x00f\x00o\x00x\x00 \x00j\x00u\x00m\x00p\x00e\x00d\x00 \x00o\x00v\x00e
\x00r\x00 \x00t\x00h\x00e\x00 \x00l\x00a\x00z\x00y\x00 \x00d\x00o\x00g\x00s\x00.
\x00\r\x00\n\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\r\x00\n\x00'
guessed encoding: utf-16
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'
('t1-ucb.txt', '')
raw: '\xfe\xff\x00T\x00h\x00e\x00 \x00q\x00u\x00i\x00c\x00k\x00 \x00b\x00r\x00o\
x00w\x00n\x00 \x00f\x00o\x00x\x00 \x00j\x00u\x00m\x00p\x00e\x00d\x00 \x00o\x00v\
x00e\x00r\x00 \x00t\x00h\x00e\x00 \x00l\x00a\x00z\x00y\x00 \x00d\x00o\x00g\x00s\
x00.\x00\r\x00\n\x00\xe0\x00\xe1\x00\xe2\x00\xe3\x00\xe4\x00\xe5\x00\r\x00\n'
guessed encoding: utf-16
1 u'The quick brown fox jumped over the lazy dogs.\r\n'
1 u'The quick brown fox jumped over the lazy dogs.'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5\r\n'
2 u'\xe0\xe1\xe2\xe3\xe4\xe5'
需要注意的事项:
(1)“mbcs”是文件系统伪编码,它与解码文件的内容完全无关。在默认编码为cp1252
的系统上,它就像latin1
(aarrgghh !!);见下文
>>> all_bytes = "".join(map(chr, range(256)))
>>> u1 = all_bytes.decode('cp1252', 'replace')
>>> u2 = all_bytes.decode('mbcs', 'replace')
>>> u1 == u2
False
>>> [(i, u1[i], u2[i]) for i in xrange(256) if u1[i] != u2[i]]
[(129, u'\ufffd', u'\x81'), (141, u'\ufffd', u'\x8d'), (143, u'\ufffd', u'\x8f')
, (144, u'\ufffd', u'\x90'), (157, u'\ufffd', u'\x9d')]
>>>
(2)chardet
非常擅长检测基于非拉丁文脚本(中文/日文/韩文,西里尔文,希伯来文,希腊文)的编码,但对基于拉丁语的编码不太擅长(Western / Central /东欧,土耳其语,越南语)并且根本没有阿拉伯语。
答案 1 :(得分:3)
记事本使用字节顺序标记保存Unicode文件。这意味着文件的第一个字节将是:
其他文本编辑器可能有也可能没有相同的行为,但如果您确定使用了记事本,这将为您提供一个适合自动选择编码的启发式方法。但是,所有这些序列在ANSI编码中都是有效的,因此这种启发式方法可能会出错。无法保证使用正确的编码。