如何从可能的编码列表中将Oracle VARCHAR2值转换为UTF-8?

时间:2012-10-03 21:45:05

标签: oracle unicode utf-8 character-encoding

由于遗留原因,我们在Oracle 10数据库中有一个VARCHAR2列,其中字符编码设置为AL32UTF8 - 包含一些非UTF-8值。值始终位于以下字符集之一:

  • US-ASCII
  • UTF-8
  • CP1252
  • Latin-1的

我编写了一个Perl函数来修复数据库外的破坏值。对于此数据库列中的值,它会遍历此编码列表并尝试将值转换为UTF-8。如果转换失败,则尝试下一次编码。第一个无错误转换的是我们保留的价值。现在,我想在数据库中复制此功能,以便任何人都可以使用它。

但是,我能找到的就是CONVERT function,它永远不会失败,但会为它无法识别的字符插入替换字符。因此,据我所知,无法知道转换何时失败。

因此,我有两个问题:

  1. 是否有一些现有的接口尝试将字符串转换为编码列表之一,返回第一个成功的字符串?
  2. 如果没有,是否有其他界面指示失败,如果它无法将字符串转换为编码?如果是这样,那么我可以写上一个函数。

  3. 更新

    作为参考,我在PL / pgSQL中编写了这个PostgreSQL函数,它完全符合我的需要:

    CREATE OR REPLACE FUNCTION encoding_utf8(
        bytea
    ) RETURNS TEXT LANGUAGE PLPGSQL STRICT IMMUTABLE AS $$
    DECLARE
        encoding TEXT;
    BEGIN
        FOREACH encoding IN ARRAY ARRAY[
            'UTF8',
            'WIN1252',
            'LATIN1'
        ] LOOP
            BEGIN
                RETURN convert_from($1, encoding);
            EXCEPTION WHEN character_not_in_repertoire OR untranslatable_character THEN
                CONTINUE;
            END;
        END LOOP;
    END;
    $$;
    

    我非常想知道如何在Oracle中做同等的事情。

2 个答案:

答案 0 :(得分:7)

感谢关于@collapsar的UTF-8非法字符的关键信息,以及同事的一些挖掘,我想出了这个:

CREATE OR REPLACE FUNCTION reencode(string IN VARCHAR2) RETURN VARCHAR2
AS
    encoded VARCHAR2(32767);
    type  array_t IS varray(3) OF VARCHAR2(15);
    array array_t := array_t('AL32UTF8', 'WE8MSWIN1252', 'WE8ISO8859P1');
BEGIN
    FOR I IN 1..array.count LOOP
        encoded := CASE array(i)
            WHEN 'AL32UTF8' THEN string
            ELSE CONVERT(string, 'AL32UTF8', array(i))
        END;
        IF instr(
            rawtohex(
                utl_raw.cast_to_raw(
                    utl_i18n.raw_to_char(utl_raw.cast_to_raw(encoded), 'utf8')
                )
            ),
            'EFBFBD'
        ) = 0 THEN
            RETURN encoded;
        END IF;
    END LOOP;
    RAISE VALUE_ERROR;
END;

奇怪的是,它永远不会到达WE8ISO8859P1:WE8MSWIN1252转换了我所拥有的800个左右的错误值列表中的每一个而没有抱怨。我的Perl或PostgreSQL实现也是如此,其中CP1252因某些值失败但ISO-8859-1成功。尽管如此,Oracle的值似乎已经足够了,并且似乎是有效的Unicode(通过将它们加载到PostgreSQL中进行测试),所以我不能抱怨。我认为这足以清理我的数据。

答案 1 :(得分:2)

要检查数据库列是否包含无效的utf-8,请使用以下查询:

 select CASE
            INSTR (
                  RAWTOHEX (
                      utl_raw.cast_to_raw (
                          utl_i18n.raw_to_char (
                                utl_raw.cast_to_raw ( <your_column> )
                              , 'utf8'
                          )
                      )
                  )
                , 'EFBFBD'
            )
        WHEN 0 THEN 'OK'
        ELSE 'FAIL' 
        END
   from <your_table>
      ;

鉴于你的db charset是al32utf8。

请注意,EF BF BD代表illegal encoding in utf-8

由于您指示的所有其他字符集都是面向字节的,因此转换为unicode永远不会失败,但可能产生不同的代码点。没有上下文信息将无法自动确定实际的源字符集。

最好的问候,carsten

PS: 字符串的oracle名称: CP1252 - &gt; WE8MSWIN1252 LATIN-1 - &gt; WE8ISO8859P1