我们遇到的问题是文本以某种不同的方式编码,但保存在表格的单个列中。很长的故事。在MySQL上,我可以“从表格中选择十六进制(str)”,我看到字符串的字节完全按照我设置的那样。
在Oracle上,我有一个以土耳其字符İ开头的字符串,它是Unicode字符0x0130“带有上面点的拉丁文大写字母”。这是我的Unicode 2.0版书的印刷版。在UTF-8中,该字符为0xc4b0。
我们需要支持非常旧的客户端应用。他们会在“windows-1254”中将此文本发送给我们。我们过去只是闭上眼睛,存放它,然后再将其交还。现在我们需要Unicode,或者被赋予Unicode。
所以我有:
SQL> select id, name from table where that thing;
ID NAME
------ ------------------------
746 Ý
这是有道理的,因为“İ”在Windows-1254中是0xdd而wondows-1252中的0xdd是“Ý”。我的终端大概设置为通常的windows-1252。
可是:
SQL> select id, rawtohex(name) from table where that thing;
ID RAWTOHEX(NAME)
------ ------------------------
746 C39D
似乎没有相当于MySQL中的十六进制(名称)功能。但我必须缺少一些东西。我在这里缺少什么?
我的java代码必须使用我提供的utf8并保存utf8副本和windows-1252副本。 java代码给了我:
bytes (utf8): c4 b0
bytes (1254): dd
然而,当我保存它时,客户端没有得到正确的字符。当我试图查看Oracle实际存储的内容时,我会看到上面看到的垃圾。我有没有想法来自C39D的来源。有什么建议吗?
我们在所有应用程序中都内置了ojdbc14.jar,并且我们正在连接到一个数据库,该数据库表示它是“Oracle Database 11g企业版版本11.2.0.2.0 - 64位生产”。
答案 0 :(得分:19)
使用dump
功能查看Oracle如何在内部存储数据。
您似乎对Oracle如何处理VARCHAR2
字符设置转换产生了误解:您无法影响Oracle如何存储其数据物理。 (如果您还没有,请阅读:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets)。
您的客户端仅以二进制方式与Oracle通信。事实上,所有系统仅以二进制形式交换信息。为了相互理解,两个系统都必须知道正在使用的语言(字符集)。
在您的情况下,我们可以重建发生的事情:
dd
发送给Oracle并说它是windows-1252
(而不是1254)
。Ý
。由于Oracle已在UTF-8
中设置,因此会将此数据转换为UTF-8
的{{1}}二进制代表:
Ý
Oracle内部存储SQL> SELECT rawtohex('Ý') FROM dual;
RAWTOHEX('Ý')
--------------
C39D
。
正如您所看到的,问题来自第一步:存在设置问题。只要你不解决这个问题,系统将无法成功对话。
使用C39D
时转换为自动,因为此数据类型是逻辑文本符号接口(您无法控制强制存储的实际二进制数据)。
答案 1 :(得分:5)
我开始使用UTF-8中的字节。
String strFromUTF8 = new String(bytes, "UTF8");
byte[] strInOldStyle = strFromUTF8.getBytes("Cp1254");
使用MySQL,我完成了。我获取这些字节,将它们转换为十六进制字符串并使用unhex(hexStr)进行更新。这允许我将遗留字节放入varchar列。
使用Oracle,我必须这样做:
String again = new String(strInOldStyle, "Cp1254");
byte[] nextOldBytes = again.getBytes("UTF8");
现在,我可以进行更新并使用以下命令将字节输入varchar2列:
update table set colName = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('hexStr')) where ...
奇怪,不是吗?我相信我已经使它变得比它需要的更复杂。
我们看到的是这个,
"İ" in UTF-8 == 0xc4d0
"İ" in Cp1254 == 0xdd == "Ý" in Cp1252
"Ý" in UTF-8 == 0xc3d9
所以,如果我收到字符串“İ”并执行:
update table set name = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('C3D9')) where ...
然后我们的遗留客户给了我们一个“İ”。是的。它有效。