11

レガシーの理由から、Oracle 10データベースには、文字エンコードが設定されているVARCHAR2列があり、AL32UTF8UTF-8以外の値が含まれています。値は常に次のいずれかの文字セットに含まれます。

  • US-ASCII
  • UTF-8
  • CP1252
  • ラテン-1

私はデータベースの外で壊れた値を修正するためにPerl関数を書きました。このデータベース列の値については、このエンコーディングのリストをループし、値をUTF-8に変換しようとします。変換が失敗した場合、次のエンコードを試みます。エラーなしで最初に変換するのは、保持する値です。ここで、この機能をデータベース内に複製して、誰でも使用できるようにしたいと思います。

ただし、これについて私が見つけることができるのは、CONVERT関数であり、失敗することはありませんが、認識できない文字の代わりに文字を挿入します。したがって、私が知る限り、変換がいつ失敗したかを知る方法はありません。

そのため、2つの質問があります。

  1. 文字列をエンコーディングのリストの1つに変換して、成功した最初のインターフェイスを返す既存のインターフェイスはありますか?
  2. そうでない場合は、文字列をエンコーディングに変換できない場合に失敗を示す他のインターフェイスはありますか?もしそうなら、私は前の関数を書くことができます。

アップデート:

参考までに、私はこのPostgreSQL関数をPL / pgSQLで記述しました。これは、必要なことを正確に実行します。

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で同等の方法を実行する方法を知りたいと思います。

4

2 に答える 2

9

@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にロードしてテスト済み)であるように見えるので、文句を言うことはできません。これは私のデータをサニタイズするのに十分だと思います。

于 2012-10-10T00:15:16.730 に答える
3

データベース列に無効な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文字セットがal32utf8であると仮定します。

utf-8でEF BF BDの不正なエンコーディングを表すことに注意してください。

指定する他のすべての文字セットはバイト指向であるため、Unicodeへの変換が失敗することはありませんが、異なるコードポイントが生成される可能性があります。コンテキスト情報がないと、実際のソース文字セットを自動で判別することはできません。

よろしく、カルステン

ps:文字セットのオラクル名: CP1252-> WE8MSWIN1252 LATIN-1->WE8ISO8859P1

于 2012-10-09T11:47:39.767 に答える