【问题标题】:Why does Oracle convert U+2002 to U+0020 when loading UTF8 data?为什么Oracle在加载UTF8数据时将U+2002转为U+0020?
【发布时间】:2013-02-28 09:46:13
【问题描述】:

将 blob 数据转换为使用 UTF8 作为字符集的 clob 数据时, 看来 Oracle (11gR2) 将字节 0xE2 0x80 0x82 转换为 0x20。 正如我所怀疑的那样,这是完全错误的吗?甚至可能是一个错误?

我需要编辑包含 UTF8 编码数据的 clob,同时保留原始字符(所有字符,尤其是 EN 空间)。

仅供参考:0xE2 0x80 0x82 是“EN 空间”,而 0x20 是“正常空间”

http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_lob.htm#i1020356

http://en.wikipedia.org/wiki/Space_%28punctuation%29

http://www.utf8-chartable.de/unicode-utf8-table.pl?start=8192

declare
    v_clob                  clob;
    v_clob_content          varchar2(8);
    v_content_ok            varchar2(64);
    v_content_bad           varchar2(64);
    v_blob                  blob;
    v_raw                   raw(8);

    v_c_dst_offset          pls_integer := 1;
    v_c_src_offset          pls_integer := 1;
    v_c_ctx                 pls_integer := 0;
    v_c_warn                pls_integer;
begin

    -- Create temporary lobs and cache them
    dbms_lob.createtemporary(v_blob,true);
    dbms_lob.createtemporary(v_clob,true);

    -- Write 0xE2 0x80 0x82 to the BLOB
    v_raw := hextoraw('E2') || hextoraw('80') || hextoraw('82');
    dbms_lob.write(v_blob,utl_raw.length(v_raw),1,v_raw);

    -- Convert the BLOB to CLOB using AL32UTF8 as encoding
    dbms_lob.converttoclob(
       v_clob,
       v_blob,
       dbms_lob.getlength(v_blob),
       v_c_dst_offset,
       v_c_src_offset, 
       nls_charset_id('AL32UTF8'),
       v_c_ctx,
       v_c_warn);

    -- Put the CLOB contents into a varchar2
    v_clob_content := dbms_lob.substr(v_clob,dbms_lob.getlength(v_clob));   

    -- Output the HEX value of respectively the BLOB content and the CLOB content
    select rawtohex(v_clob_content) into v_content_bad from dual;
    select rawtohex(v_raw) into v_content_ok from dual;
    dbms_output.put_line('[' || v_content_bad || ']');
    dbms_output.put_line('[' || v_content_ok || ']');

    -- Release resources
    dbms_lob.freetemporary(v_clob);
    dbms_lob.freetemporary(v_blob);
end;
/

输出:

[20]
[E28082]

PL/SQL procedure successfully completed.

【问题讨论】:

  • CLOB 经常用于存储 SQL 查询。 “EN Space”到“Normal Space”的转换可能已经完成,因为 SQL 解析器可能不会将“EN Space”解释为空格。
  • 我不同意,即使 CLOB 可以用来存储 SQL 查询,也很难成为更改 clob 内容的充分理由。此外,CLOB 当然不打算单独保存 SQL 查询。

标签: oracle utf-8 character-encoding plsql


【解决方案1】:

您的数据库可能没有在AL32UTF8 字符集下运行?

即:

SQL> col value format a20
SQL> select * from nls_database_parameters where parameter like '%CHARACTERSET%';

PARAMETER                      VALUE
------------------------------ --------------------
NLS_CHARACTERSET               AL32UTF8
NLS_NCHAR_CHARACTERSET         UTF8

SQL> declare
  2      v_clob                  clob;
  3      v_clob_content          varchar2(8);
  4      v_content_ok            varchar2(64);
  5      v_content_bad           varchar2(64);
  6      v_blob                  blob;
  7      v_raw                   raw(8);
  8
  9      v_c_dst_offset          pls_integer := 1;
 10      v_c_src_offset          pls_integer := 1;
 11      v_c_ctx                 pls_integer := 0;
 12      v_c_warn                pls_integer;
 ...
 46  end;
 47  /
[E28082]
[E28082]

PL/SQL procedure successfully completed.

vs 在非 al32utf8 数据库中运行:

SQL> select * from nls_database_parameters where parameter like '%CHARACTERSET%';

PARAMETER                      VALUE
------------------------------ --------------------
NLS_NCHAR_CHARACTERSET         UTF8
NLS_CHARACTERSET               WE8ISO8859P15

SQL> set serverout on
SQL> declare
  2      v_clob                  clob;
...
 47  /
[BF]
[E28082]

lob 已在我的西方 ISO 数据库中转换。如果您需要将AL32UTF8 保留为非二进制数据类型,请确保您的数据库在该字符集下运行。

【讨论】:

  • 谢谢,作为一个附带问题:我无法更改数据库字符集,没有其他选择吗?
  • @CharlesV.G.唯一的另一件事是 NLS 字符集,即与 NVARCHAR2/NCLOB/NCHAR 数据类型一起使用,但这些仅限于 UTF8/UTF16(不完全是 AL32UTF8,所以你可能仍然会遇到一些问题。你的 DBA可以在不重建整个数据库的情况下更改 NLS 字符集。除此之外,不会,因为数据库在以非二进制形式存储时总是会转换数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多