我可以看到一个Python字符串(不是unicode字符串,即u'')输入\xe4\xb8\xad\xe6\x96\x87
,中文
将其存储在Oracle表中后更改为\xe4\xb8\xad\xe6\xbf\xbf
其列是使用WE8MSWIN1252
字符集的CLOB(VARCHAR),并使用Web框架在Python中检索它。我正在尝试排除故障并了解低级别的情况。
为什么数据库会改变我的输入 - 我认为其中的内容也会出现?
我被告知,"这是因为WE8MSWIN1252是单字节系统,它不支持多字节编码"。这对我来说有点高级别的解释。多字节仍然是3个字节。那么为什么WE8MSWIN1252
系统可以将其分解为3个不同的字节,如果\xe6,\x96 and \x87
存储它并留给最终用户来解释它?
单字节编码不能容纳多字节系统是什么意思?我的理解中缺少什么?它的全部是1和0。我们存储位而不是Unicode等 - 这是一个更高级别的抽象?
答案 0 :(得分:2)
编码字符串的重点是你不只存储位,你存储字符串。 Oracle的Choosing a character set文档根据Oracle数据库确切地解释了这意味着什么。
单字节编码只知道如何表示(最多)256个不同的字符。如果你给它一个不同的角色,那就无法表现出来。它应该做什么呢?
您建议的是它应该采用某些其他编码中的表示形式,并假装字节是其自身编码中的字符。这不仅在概念上没有意义,它实际上并不起作用 - 事实上,这正是mojibake的意思。
举一个具体的例子,你有一个WE8MSWIN1252(大致相同的编码Python调用cp1252)字符串列。您想要存储字符串'中文'
。没有cp1252,但它有一个UTF-8,它是'\xe4\xb8\xad\xe6\x96\x87'
。
那么,如果你只是存储了UTF-8字节,好像它们是cp1252字符呢?嗯,这取决于您的数据库如何定义代码页1252。最后两个字节在原始代码页1252中不是有效字符,但是当前Windows代码页1252确实将它们映射到字符。因此,如果数据库遵循IBM规则,它应该给您一个错误,或者用"无效字符替换字节"表示(至少对于具有这种事物的编码); *如果它试图模仿Windows,它应该允许它。**
为了避免这个问题,让我们更简单一点:如果你选择一个完整的256个字符的基于Latin-1的编码怎么办?这会欺骗它允许你存储数据。然后你要存储字符串'ä¸æ'
或'ä¸\xadæ–‡'
或类似的东西。这似乎并不十分有用。您可以编写一个应用程序,这样做是有意义的(因为您知道您将通过Latin-1 mojibake重新编码为UTF-8到每一侧的真实字符串),但在这种情况下,您为什么要使用一个字符串在第一位?只需使用二进制列,并跳过整个Latin-1部分,并由应用程序知道二进制数据意味着UTF-8,而不是由应用程序知道看起来像拉丁语-1必须被重新编码才能用作UTF-8。
或者,更简单,只需使用UTF-8列,或者停止尝试将东亚文本存储在cp1252列中......
*请参阅the docs了解Oracle如何解释替换字符,这比您预期的要复杂得多,与Python不同。
**您的数据库调用编码WE8MSWIN1252
的事实似乎暗示应该使用Windows定义;它将它们转换为0xBF这一事实意味着它不会这样做。这可能是合理的,因为" MSWIN"他们的意思是" MS Windows 3.1"或者,地狱,甚至" MS Windows 1.0",但我真的不知道。无论如何,正如下一段所解释的那样,它并不重要。如果您想查看在"代码页1252"的任何含义下不应该合法的字符会发生什么情况,请参阅try '東京'
,这是UTF中的'\xe6\x9d\xb1\xe4\xba\xac'
8,在cp1252中故意将0x9D留空。