我们有一个老客户正在与旧版服务器应用程序对话。在土耳其,它将文本发送为Windows-1254。我们将其存储并发回。

数据库中的行名称为“İ”,该名称为大写字母I,并在其顶部带有一个点。在Windows-1254中,这是一个0xdd字符,在UTF-8中,这是0xc4b0。

如果我查看数据库,则会看到以下内容:

SQL> select dump(name, 16) from thing where other thing;

DUMP(NAME,16)
--------------------------------------------------------------------------------
Typ=1 Len=2: c3,9d

奇怪的。正如有人在另一个问题中指出的那样...

在Windows-1254中,“İ”字符为0xdd。事实证明,在Windows-1252中,0xdd是“Ý”字符,在UTF-8中是0xc39d。因此,我们看到的东西被抛弃了。

我们认为我们想要做的是,但这显然行不通:
SQL> update thing set name = UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.CONVERT(HEXTORAW('dd'), 'CP1254', 'UTF8')) where otherthing;
update thing set name = UTL_RAW.CAST_TO_VARCHAR2(UTL_RAW.CONVERT(HEXTORAW('dd'), 'CP1254', 'UTF8')) where otherthing
                                                        *
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at "SYS.UTL_RAW", line 327

??

许多文档讨论了UTL_RAW.CONVERT,没有人显示其实际使用示例。这是为什么?是否有允许的NLS字符集名称的列表?我找不到一个。有什么建议么?我可以找到Java知道的所有编码的列表,但是找不到Oracle的此列表。

我有一个Java应用程序,用于编写必须由旧版软件读取的数据。该应用程序收到UTF-8。我可以成功地将其转换为Windows-1254字节。我可以将它们翻译成Windows-1252。然后,如果将它们转换为UTF-8,则可以使用以下命令将其写入数据库:
SQL> update this set name = UTL_RAW.CAST_TO_VARCHAR2(hextoraw('c39d')) where otherthing;

1 row updated.

SQL> select dump(name, 16) from thing where otherthing;

DUMP(NAME,16)
--------------------------------------------------------------------------------
Typ=1 Len=2: c3,9d

客户端将这一行显示为“İ”。但是,你知道的,哇。这似乎是荒谬的。但是,如果那是可行的,那可能就是必须要发生的事情...。

最佳答案

根据UTL_RAW,文档中没有解释的是,一个字符集由三部分组成: NLS_LANGUAGE,NLS_TERRITORY和字符集本身。要查看有效值的列表,可以查询 V$NLS_VALID_VALUES 。语言和领土的完整列表也可以在the documentation中找到。

这引发了您的第一个问题。根据Oracle Win-1254,不是CP1254,而是TR8MSWIN1254。同样,尽管存在UTF8字符集,但我怀疑您的数据库是使用AL32UTF8设置的。您可以通过查询 NLS_DATABASE_PARAMETERS 进行双重检查。

因此,如果这是土耳其语,而您在土耳其,则假定您的语言和地区就是TURKISH_TURKEY

现在,将其添加到角色集即可返回所需的内容:

select utl_raw.convert( hextoraw('dd')
                      , 'TURKISH_TURKEY.AL32UTF8'
                      , 'TURKISH_TURKEY.TR8MSWIN1254'
                      ) as raw_char
  from dual;

RAW_CHAR
-----------------------------------------------------

C4B0

正如您已经注意到的,0xc4b0是UTF-8中İ的表示形式,因此您可以按预期使用UTL_RAW.CAST_TO_VARCHAR2 1:
select utl_raw.cast_to_varchar2(
           utl_raw.convert( hextoraw('dd')
                          , 'TURKISH_TURKEY.AL32UTF8'
                          , 'TURKISH_TURKEY.TR8MSWIN1254'
                            )) as new_char
  from dual;

1.我没有可以表示此字符的基于文本的Oracle客户端;对不起!

09-27 17:18