我在理解读取和写入文件的文本方面有一些大脑故障(Python 2.4)。
# The string, which has an a-acute in it.
ss = u'Capit\xe1n'
ss8 = ss.encode('utf8')
repr(ss), repr(ss8)
(“u’Capit \ xe1n’”、“’Capit \ xc3 \ xa1n’")
print ss, ss8
print >> open('f1','w'), ss8
>>> file('f1').read()
'Capit\xc3\xa1n\n'
于是我在我最喜欢的编辑器f2文件中输入了大写字母\xc3\xa1n。
然后:
>>> open('f1').read()
'Capit\xc3\xa1n\n'
>>> open('f2').read()
'Capit\\xc3\\xa1n\n'
>>> open('f1').read().decode('utf8')
u'Capit\xe1n\n'
>>> open('f2').read().decode('utf8')
u'Capit\\xc3\\xa1n\n'
我哪里不明白了?显然,我忽略了一些至关重要的魔力(或良好的感觉)。在文本文件中输入什么才能得到正确的转换?
我在这里真正搞不懂的是,当它来自外部时,如果你不能让Python识别它,那么UTF-8表示的意义是什么。也许我应该只是JSON转储字符串,并使用它,因为它有一个asciiable表示!更重要的是,当这个Unicode对象从文件中传入时,是否存在Python能够识别和解码的ASCII表示形式?如果有,我怎么得到它?
>>> print simplejson.dumps(ss)
'"Capit\u00e1n"'
>>> print >> file('f3','w'), simplejson.dumps(ss)
>>> simplejson.load(open('f3'))
u'Capit\xe1n'
您曾经遇到过编码的一般问题:如何判断文件采用的是哪种编码?
答:不能,除非文件格式提供了这一点。例如,XML的开头是:
<?xml encoding="utf-8"?>
此标头经过精心选择,以便无论编码如何都可以读取。在你的例子中,没有这样的提示,因此你的编辑器和Python都不知道发生了什么。因此,您必须使用codecs模块并使用codecs.open(path,mode,encoding),它提供了Python中缺少的位。
至于编辑器,必须检查它是否提供了设置文件编码的方法。
UTF-8的重点是能够将21位字符(Unicode)编码为8位数据流(因为这是世界上所有计算机都能处理的唯一事情)。但是由于大多数操作系统早于Unicode时代,它们没有合适的工具将编码信息附加到硬盘上的文件。
下一个问题是Python中的表示。这在heikogerlach的评论中得到了完美的解释。您必须理解控制台只能显示ASCII。为了显示Unicode或任何东西>= charcode 128,它必须使用一些转义的方法。在编辑器中,您不能输入转义的显示字符串,而是输入字符串的含义(在这种情况下,您必须输入变音符并保存文件)。
也就是说,你可以使用Python函数eval()将转义字符串转换为字符串:
>>> x = eval("'Capit\\xc3\\xa1n\\n'")
>>> x
'Capit\xc3\xa1n\n'
>>> x[5]
'\xc3'
>>> len(x[5])
1
如您所见,字符串“\xc3”已被转换为单个字符。这现在是一个8位字符串,UTF-8编码。获取Unicode:
>>> x.decode('utf-8')
u'Capit\xe1n\n'
Gregg Lind问:我认为这里缺少了一些内容:文件f2包含:hex:
0000000: 4361 7069 745c 7863 335c 7861 316e Capit\xc3\xa1n
编解码器。open('f2','rb', 'utf-8'),例如,在一个单独的字符中读取它们(预期)是否有任何方法可以用ASCII写入文件?
答:那要看你是什么意思了。ASCII不能表示字符> 127。因此,您需要某种方法来表示“接下来的几个字符意味着一些特殊的东西”,这就是序列“\x”所做的。它说:接下来的两个字符是一个字符的代码。“\u”使用四个字符将Unicode编码到0xFFFF(65535)。
因此,您不能直接将Unicode写入ASCII(因为ASCII不包含相同的字符)。你可以把它写成字符串转义(如f2);在这种情况下,文件可以用ASCII表示。或者你可以把它写成UTF-8,在这种情况下,你需要一个8位的安全流。
使用decode('string-escape')的解决方案确实有效,但你必须意识到你使用了多少内存:是使用co解码器.open()的三倍。
记住,文件只是一个8位字节的序列。比特和字节都没有意义。是你说“65意味着‘A’”。由于\xc3\xa1应该变成“à”,但计算机没有办法知道,你必须通过指定写入文件时使用的编码来告诉它。
与其混淆.encode和.decode,不如在打开文件时指定编码。在Python 2.6中添加的io模块提供了一个io。Open函数,允许指定文件的编码。
假设文件用UTF-8编码,我们可以使用:
>>> import io
>>> f = io.open("test", mode="r", encoding="utf-8")
然后f.read返回一个解码后的Unicode对象:
>>> f.read()
u'Capit\xe1l\n\n'
在3。X, io。Open function是内置Open函数的别名,它支持encoding参数(在2.x中不支持)。
我们也可以使用codecs标准库模块中的open:
>>> import codecs
>>> f = codecs.open("test", "r", "utf-8")
>>> f.read()
u'Capit\xe1l\n\n'
但是请注意,当混合使用read()和readline()时,这可能会导致问题。