17

我们遇到了以某些不同方式编码但保存在表中的单个列中的文本的问题。很长的故事。在 MySQL 上,我可以执行“从表中选择 hex(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 中的 hex(name) 函数等效的功能。但我一定是错过了什么。我在这里想念什么?

我的 java 代码必须采用我提供的 utf8 并保存一个 utf8 副本和一个 windows-1252 副本。java代码给了我:

bytes (utf8):  c4 b0
bytes (1254):  dd

然而,当我保存它时,客户端没有得到正确的字符。当我尝试查看 Oracle 实际存储的内容时,我得到了上面看到的垃圾。我知道 C39D 是从哪里来的。有什么建议么?

我们在所有应用程序中都内置了 ojdbc14.jar,并且我们正在连接到一个数据库,该数据库显示它是“Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production”。

4

2 回答 2

20

使用该dump函数查看 Oracle 如何在内部存储数据。

您似乎对 Oracle 如何处理VARCHAR2字符集转换存在误解:您无法影响 Oracle 如何物理存储其数据。(另外,如果您还没有,阅读:每个软件开发人员绝对、肯定必须了解 Unicode 和字符集的绝对最低要求会很有帮助)。

您的客户仅以二进制形式与 Oracle 对话。事实上,所有系统都只以二进制交换信息。为了相互理解,两个系统都必须知道正在使用什么语言(字符集)。

在您的情况下,我们可以重建发生的情况:

  1. 您的客户端将字节发送dd到 Oracle 并说它是windows-1252(而不是1254).
  2. Oracle 查找其字符集表,发现该数据被转换Ý为该字符集中的符号。
  3. Oracle将此信息逻辑地存储在其表中。
  4. 由于 Oracle 在 中设置UTF-8,因此它将这些数据转换为 的UTF-8二进制表示Ý

    SQL> SELECT rawtohex('Ý') FROM dual;
    
    RAWTOHEX('Ý')
    --------------
    C39D
    
  5. OracleC39D内部存储。

如您所见,问题来自第一步:设置有问题。只要您不解决此问题,系统将无法成功对话。

使用时转换是自动VARCHAR2的,因为此数据类型是逻辑文本符号接口(您几乎无法控制强制存储实际二进制数据)。

于 2013-09-09T16:19:08.363 回答
6

我有 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 ...

然后我们的旧客户端给了我们一个“İ”。是的。有用。

于 2013-09-17T22:59:04.363 回答