Как преобразовать значения Oracle VARCHAR2 в UTF-8 из списка возможных кодировок?
по устаревшим причинам у нас есть столбец VARCHAR2 в нашей базе данных Oracle 10-где кодировка символов установлена в AL32UTF8
-которые содержат какие-либо не-UTF-8 значений. Значения всегда находятся в одном из следующих наборов символов:
- US-ASCII
- UTF-8
- CP1252
- латинский-1
Я написал функцию Perl для исправления сломанных значений вне базы данных. Для значения из этого столбца базы данных он проходит через этот список кодировки и пытается преобразовать значение в UTF-8. Если преобразование не удается, он пытается следующую кодировку. Первое, что нужно преобразовать без ошибок, - это значение, которое мы сохраняем. Теперь я хотел бы реплицировать эту функциональность внутри базы данных, чтобы любой мог ее использовать.
однако, все, что я могу найти на это CONVERT
функции, который никогда не терпит неудачу, но вставляет символ замены для символов, которые он не распознает. Так что, насколько я могу судить, узнать это невозможно. когда преобразование не удалось.
поэтому у меня есть два вопроса:
- есть ли какой-то существующий интерфейс, который пытается преобразовать строку в один из списков кодировок, возвращая первый, который преуспевает?
- а если нет, есть ли другой интерфейс, который указывает на сбой, если он не может преобразовать строку в кодировку? Если да, то я мог бы написать предыдущий функция.
обновление:
Для справки, я написал эту функцию 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(, encoding);
EXCEPTION WHEN character_not_in_repertoire OR untranslatable_character THEN
CONTINUE;
END;
END LOOP;
END;
$$;
Я бы очень хотел знать, как сделать эквивалент в Oracle.
2 ответов
благодаря ключевой информации о незаконных символах в UTF-8 от @collapsar, а также некоторым раскопкам коллегой, я придумал это:
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), поэтому я не могу жаловаться. Думаю, этого будет достаточно, чтобы санировать мои данные.
чтобы проверить, содержит ли столбец базы данных недопустимый 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>
;
учитывая, что ваша кодировка БД-al32utf8.
отметим, что EF BF BD
представляет собой незаконное кодирование в utf-8.
поскольку все другие кодировки, которые вы указываете, ориентированы на байт, преобразование в unicode никогда не завершится неудачей, но, возможно, приведет к различным кодовым точкам. без контекстной информации автоматизированное определение фактического источника кодировка невозможна.
С наилучшими пожеланиями, Карстен
ps:
имена oracle для кодировок:
CP1252
->WE8MSWIN1252
LATIN-1
->WE8ISO8859P1