【问题标题】:Oracle: Convert xml entities in a varchar2 field to utf-8 charactersOracle:将 varchar2 字段中的 xml 实体转换为 utf-8 字符
【发布时间】:2012-02-02 14:43:44
【问题描述】:

我在一个表中有一个字段,其中包含特殊字符的 XML 实体,因为该表是 latin-1 格式的。 例如。 “Hallöle slovenčina”(“ö”在 latin-1 中,但“slovenčina”中的“č”必须由某些将值存储到数据库中的应用程序转换为实体)

现在我需要通过将 XML 实体转换为其原始字符,将表格导出为 utf-8 编码文件。

Oracle 中是否有一个函数可以为我处理这个问题,还是我真的需要为此创建一个巨大的键/值映射?

非常感谢任何帮助。

编辑:我找到了函数DBMS_XMLGEN.convert,但它只适用于<>&。不在&#NNN; :-(

【问题讨论】:

  • this 链接dbms_xmlgen.convert 转换了“'”。可能是因为是 xml 表而不是 varchar2?
  • Sérgio,它绝对是一个 varchar2 字段:CREATE TABLE mytable (tid INTEGER NOT NULL, zitat VARCHAR2 (2000) NOT NULL);

标签: oracle utf-8 latin1


【解决方案1】:

我认为 dbms_xmlgen 的问题在于技术上只有五个 XML 实体。您的示例有一个数字 HTML 实体,对应于 Unicode:

http://theorem.ca/~mvcorks/cgi-bin/unicode.pl.cgi?start=0100&end=017F

Oracle 有一个函数 UNISTR,这在这里很有帮助:

select unistr('sloven\010dina') from dual;

在上面的示例中,我已将 269 转换为等效的十六进制 010d(在 Unicode 中为 U+010D)。但是,您可以传递一个十进制数并进行如下转换:

select unistr('sloven\' || replace(to_char(269, 'xxx'), ' ', '0') || 'ina') from dual;

编辑:PL/SQL 解决方案:

这是我为你准备的一个例子。这应该循环并替换您从表中选择的每一行的任何匹配项。

create table html_entities (
    id NUMBER(3),
    text_row VARCHAR2(100)
);

INSERT INTO html_entities 
VALUES (1, 'Hallöle slovenčina Ċ ú');

INSERT INTO html_entities 
VALUES (2, 'I like the letter Ċ');

INSERT INTO html_entities 
VALUES (3, 'Nothing to change here.');

DECLARE
    v_replace_str NVARCHAR2(1000);
    v_fh UTL_FILE.FILE_TYPE;       
BEGIN
    --v_fh := utl_file.fopen_nchar(LOCATION IN VARCHAR2, FILENAME IN VARCHAR2, OPEN_MODE IN VARCHAR2, MAX_LINESIZE IN BINARY_INTEGER);

    FOR v_rec IN (select id, text_row from html_entities) LOOP
        v_replace_str := v_rec.text_row;
        WHILE (REGEXP_INSTR(v_replace_str, '&#[0-9]+;') <> 0) LOOP
            v_replace_str := REGEXP_REPLACE(
                v_replace_str, 
                '&#([0-9]+);',
                unistr('\' || replace(to_char(to_number(regexp_replace(v_replace_str, '.*?&#([0-9]+);.*$', '\1')), 'xxx'), ' ', '0')),
                1,
                1
            );
        END LOOP;

        -- utl_file.put_line_nchar(v_fh, v_replace_str);
        dbms_output.put_line(v_replace_str);

    END LOOP;
    --utl_file.fclose(v_fh);
END;
/

请注意,我已在对 UTL_FILE 函数的调用中存根,以将 NVARCHAR 行(Oracle 的扩展字符集)写入数据库服务器上的文件。 dbms_output 虽然非常适合调试,但似乎不支持扩展字符,但如果您使用 UTL_FILE 写入文件,这应该不是问题。这是 DBMS_OUTPUT:

Hallöle slovencina C ú
I like the letter C
Nothing to change here.

【讨论】:

  • "unistr" 看起来像一个有趣的函数,但由于我的表中有很多行,其中包含许多不同字符的实体,所有这些都采用 N; 格式。 (其中 N 代表一个或多个十进制数字),这种方法对我来说似乎不可行。
  • @mawimawi - 我意识到这不是一个完整的解决方案,但它是一个好的开始。我看到 MK 使用正则表达式扩展了我的解决方案。如果REGEXP_REPLACE 没有全局替换功能(如果没有,那就太可惜了),您可以使用存储过程 - 遍历游标,并在每个行循环中,直到您不再使用REGEXP_INSTR 函数以及 REGEXP_REPLACE。如果我以后有时间,你仍然没有解决方案,我可以写一个代码示例。
  • 如果你有时间,那将是我的天赐之物。
  • 感谢您的帮助 Danimal37!看起来棒极了!
  • 谢谢!我卑微的名声刚刚有了很大的飞跃! :)
【解决方案2】:

你也可以只使用国际化包:

UTL_I18N.unescape_reference('文本')

非常适合将这些 html 实体更改为普通字符(例如将数据库从 iso 8859P1 移动到 UTF-8 后进行清理)

【讨论】:

    【解决方案3】:

    这可能应该在我不知道的 PL/SQL 中完成,但我想看看我能用纯 SQL 做到多远。这只会替换第一次出现的代码,因此您必须以某种方式多次运行它。

    select regexp_replace(s, '&#([0-9]+);', u) from
    (select s, unistr('\0' || REPLACE(TO_CHAR(TO_NUMBER(c), 'xxxx'), ' ', '')) u from
    (select s, regexp_replace(s, '.*&#([0-9]+);.*', '\1') c from
    (select 'Hallöle sloven&#269;ina' s from dual)))
    

    或可读性较差但更实用:

    SELECT 
    REGEXP_REPLACE(s, '&#([0-9]+);', unistr('\0' || REPLACE(TO_CHAR(TO_NUMBER(regexp_replace(s, '.*?&#([0-9]+);.*$', '\1', 1, 1)), 'xxxx'), ' ', '')), 1, 1) 
    FROM
    (SELECT 'Hallöle sloven&#269;ina &#269; &#278;' s FROM DUAL)
    

    这个(更新的)版本正确地替换了第一个匹配项。您需要应用它,直到所有这些都被替换。

    【讨论】:

    • 看起来很棒!但是 regexp_replace 函数是否没有一个标志可以在字符串中多次替换?我很确定 perl、python 和其他语言都支持这样的标志。否则我必须多次替换短语,例如波兰语或斯洛伐克语:-(
    • 是的,它可以全部替换,但问题是您不是每次都用相同的字符串替换它们。您需要提取代码,将其转换为 unicode 并用 unicode 字符替换该代码。以某种方式使用纯 SQL 可能可以做到这一点,但这绝对超出了我的 SQL 专业水平。
    • 将“269”替换为“160”会返回must be followed by four hexadecimal characters 错误](oracleerror.com/2016/07/30/…)(在 11g 中)。知道为什么以及如何解决吗?我的 XML 字符串包含许多 &amp;#160; 等。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2011-01-05
    • 2015-02-25
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 2011-06-25
    • 1970-01-01
    • 2010-12-17
    相关资源
    最近更新 更多