【问题标题】:Displaying the hex value of a string from a oracle varchar2?显示来自 oracle varchar2 的字符串的十六进制值?
【发布时间】:2013-09-09 15:39:37
【问题描述】:

我们遇到了一些以不同方式编码但保存在表格中的单个列中的文本问题。很长的故事。在 MySQL 上,我可以执行“从 table where 选择 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”。

【问题讨论】:

    标签: oracle unicode utf-8 turkish ojdbc


    【解决方案1】:

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

    您似乎对 Oracle 如何处理 VARCHAR2 字符集转换存在误解:您无法影响 Oracle 如何以物理方式存储其数据。 (另外,如果您还没有,阅读:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets 会很有帮助。

    您的客户仅以二进制形式与 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. Oracle 在内部存储C39D

    如您所见,问题出在第一步:设置有问题。只要不解决这个问题,系统就无法成功对话。

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

    【讨论】:

    • 我认为您的评论最终会有所帮助。在我保存“旧样式”字符串之前,我必须将它转换为 java 中的字符串。我使用“ISO-8859-1”来执行此操作,因为这是“闭上眼睛并传递它”方法中使用的内容。这在将其保存到 MySQL 时有效。也许甲骨文对这里的逻辑缺陷并不那么宽容......我们会看到的。
    • 有趣的是,这个函数列表 (psoug.org/reference/convert_func.html) 没有将 dump() 列为函数之一。但是它在其他函数use dump()的一些描述中给出的例子。没那么有用.....
    • 而 dump() 转储十进制值。来吧,甲骨文,给我一块骨头!
    • @RayKiddy 您可以在转储中指定返回格式,在您的情况下为 16 为十六进制
    • 如今尝试查找 Oracle 文档真的很有趣。我在 Google 中搜索“Oracle some thing”,十分钟后,我意识到我正在阅读 MySQL 文档。有趣。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2011-08-27
      • 1970-01-01
      • 2015-11-20
      • 2012-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-13
      • 2019-07-27
      相关资源
      最近更新 更多