Python + PostgreSQL + 奇怪的ascii = UTF8编码错误
我有一些ASCII字符串,其中包含字符"\x80"
,用来表示欧元符号:
>>> print "\x80"
€
当我把包含这个字符的字符串数据插入到我的数据库时,我遇到了:
psycopg2.DataError: invalid byte sequence for encoding "UTF8": 0x80
HINT: This error can also happen if the byte sequence does not match the encodi
ng expected by the server, which is controlled by "client_encoding".
我对Unicode还不太了解。请问我该如何把包含"\x80"
的字符串转换成有效的UTF-8格式,以便能正确显示那个欧元符号呢?我尝试过在各种字符串上使用.encode
和.decode
,但总是遇到错误:
>>> "\x80".encode("utf-8")
Traceback (most recent call last):
File "<pyshell#14>", line 1, in <module>
"\x80".encode("utf-8")
UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0: ordinal not in range(128)
1 个回答
这个问题的开头就有个错误的假设:
我有一些ASCII字符串,其中包含字符"\x80",用来表示欧元符号。
其实,ASCII字符的范围是从"\x00"到"\x7F"。
之前被接受但现在已删除的回答有两个严重的误解:(1)认为地区设置等于编码(2)认为latin1编码把"\x80"映射到欧元字符。
实际上,所有的ISO-8859-x编码都把"\x80"映射到U+0080,这个是C1控制字符,而不是欧元字符。只有其中的3种编码(7, 15, 16)提供了欧元字符,表示为"\xA4"。可以参考这篇维基百科文章。
你需要知道你的数据使用了什么编码。它是在哪台机器上创建的?怎么创建的?创建时的地区设置(不一定是你的)可能会给你一些线索。
需要注意的是,“我的数据是用latin1编码的”这句话和“支票在邮寄中”以及“当然我早上会爱你”一样不可靠。你的数据可能是用Windows平台上的某种cp125x编码的。除了cp1251(Windows西里尔文)以外,所有这些编码都把"\x80"映射到欧元字符:
>>> ['\x80'.decode('cp125' + str(x), 'replace') for x in range(9)]
[u'\u20ac', u'\u0402', u'\u20ac', u'\u20ac', u'\u20ac', u'\u20ac', u'\u20ac', u'\u20ac', u'\u20ac']
更新 针对提问者的评论
我从一个文件中读取这些数据,例如
open(fname).read()
。它包含了代表欧元字符的字符串\x80。这只是一个普通的文本文件。它是由另一个程序生成的,但我不知道它是如何生成文本的。有什么好的解决方案吗?我在想我可以假设它输出"\x80"作为欧元字符,这意味着我可以假设它是用一个包含该字符作为欧元的cp125x编码。
这有点让人困惑:你先说
它包含了代表欧元字符的字符串\x80
但后来你又说
我在想我可以假设它输出"\x80"作为欧元字符
请解释一下。
选择合适的cp125x编码:这个文件是在哪个地方创建的?文本是用什么语言写的?有没有其他字符的值大于"\x7f"?如果有的话,是什么字符,它们在什么上下文中使用?
更新2 如果你不知道“程序是怎么写的”,那么你和我们都无法判断它是否总是把"\x80"用作欧元字符。虽然这样做会非常愚蠢,但也不能完全排除这种可能性。
如果文本是用英语写的,或者是在美国写的,或者是在Windows平台上写的,那么可以合理地认为cp1252
是合适的选择……直到你得到相反的证据为止,在那种情况下,你需要自己猜测编码,或者回答(什么语言,什么地区)的问题。