Oracle: преобразование объектов XML в поле varchar2 в символы utf-8 - PullRequest
4 голосов
/ 02 февраля 2012

У меня есть поле в таблице, которое содержит объекты XML для специальных символов, так как таблица находится в латинице-1.Например, "Hallöle slovenčina" ("ö" в латинице-1, но "č" в "slovenčina" пришлось преобразовать в сущность с помощью какого-либо приложения, которое сохраняет значения в базе данных)

Теперь мне нужно экспортировать таблицу в файл в кодировке utf-8 путем преобразования сущностей XML в их исходные символы.

Есть ли в Oracle функция, которая могла бы обработать это для меня, или мне действительно нужно создатьогромная карта ключ / значение для этого?

Любая помощь очень ценится.

РЕДАКТИРОВАТЬ: Я нашел функцию DBMS_XMLGEN.convert, но она работает только на <, > и&.Не включено &#NNN;: - (

Ответы [ 3 ]

7 голосов
/ 11 марта 2013

Вы также можете просто использовать пакет интернационализации:

UTL_I18N.unescape_reference ('текст')

Прекрасно работает при замене этих HTML-объектов на обычные символы (например, очистка после перемещения базы данных из iso 8859P1 в UTF-8)

6 голосов
/ 07 февраля 2012

Я считаю, что проблема с 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 в примере выше (в Юникоде это 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&#269;ina &#266; &#250;');

INSERT INTO html_entities 
VALUES (2, 'I like the letter &#266;');

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.
2 голосов
/ 07 февраля 2012

Это, вероятно, должно быть сделано в 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)

Это (обновлено)Версия правильно заменяет первое вхождение.Вы должны применять его, пока все они не будут заменены.

...