Почему коды символов EMOJI различны в браузере и в базе данных Oracle? - PullRequest
1 голос
/ 26 апреля 2019

Я сохранил символ EMOJI из веб-приложения в Oracle Database.В html этот символ кодируется символом ?.Т.е. если я напишу

<html>&#128514</html>

, я увижу это в браузере.Но в БД запрос

select ascii(sym) from ..

возвращает значение 4036991106. Вопрос в том, почему эти коды разные (128514 и 4036991106), и как конвертировать один в другой?Запрос

select value from nls_database_parameters where parameter='NLS_CHARACTERSET'

возвращает значение AL32UTF8.Запрос

select sym...

Возвращает '?', это символ, который я сохранил.

1 Ответ

1 голос
/ 26 апреля 2019

Прочитайте документацию: ASCII

ASCII возвращает десятичное представление в наборе символов базы данных первого символа char.

  • 4036991106 - это десятичное число в UTF-8 (т. Е. AL32UTF8)
  • 128514 - это десятичное число в UTF-32BE

См. Также Лицо со слезамиРадость U + 1F602

Преобразование не так тривиально, вы можете использовать этот блок.

DECLARE
    codepoint INTEGER := 128514;

    sg1 CHAR(2);
    sg2 CHAR(2);
    sg3 CHAR(2);
    sg4 CHAR(2);

    res VARCHAR2(20);
BEGIN   

    --128 = x80 = 10000000 -> 10xxxxxx
    --192 = xC0 = 11000000 -> 110xxxxx
    --224 = eE0 = 11100000 -> 1110xxxx
    --240 = xF0 = 11110000 -> 11110xxx

    IF codepoint <= 127 THEN        
        res := LPAD(TO_CHAR(codepoint, 'fmXX'), 2, '0');
    ELSIF codepoint <= 2047 THEN
        sg1 := TO_CHAR(192 + TRUNC(codepoint / 2**6), 'fmXX');
        sg2 := TO_CHAR(128 + codepoint MOD 2**6, 'fmXX');
        res := LPAD(sg1, 2, '0')||LPAD(sg2, 2, '0');
    ELSIF codepoint <= 65535 THEN
        sg1 := TO_CHAR(224 + TRUNC(codepoint / 2**12), 'fmXX');        
        sg2 := TO_CHAR(128 + TRUNC(codepoint / 2**6) MOD 2**6, 'fmXX');
        sg3 := TO_CHAR(128 + codepoint MOD 2**6, 'fmXX');
        res := LPAD(sg1, 2, '0')||LPAD(sg2, 2, '0')||LPAD(sg3, 2, '0');
    ELSE
        sg1 := TO_CHAR(240 + TRUNC(codepoint / 2**18), 'fmXX');        
        sg2 := TO_CHAR(128 + TRUNC(codepoint / 2**12) MOD 2**12, 'fmXX');
        sg3 := TO_CHAR(128 + TRUNC(codepoint / 2**6) MOD 2**6, 'fmXX');
        sg4 := TO_CHAR(codepoint MOD 2**6 + 128, 'fmXX');
        res := LPAD(sg1, 2, '0')||LPAD(sg2, 2, '0')||LPAD(sg3, 2, '0')||LPAD(sg4, 2, '0');
    END IF;
    DBMS_OUTPUT.PUT_LINE( 'Hex:'||res );
    DBMS_OUTPUT.PUT_LINE( 'Decimal: '||TO_NUMBER(res, 'XXXXXXXX') );

END;

Результат:

Hex: F09F9882
Decimal: 4036991106

Для преобразования в другойНаправление вы можете использовать эту функцию:

CREATE OR REPLACE FUNCTION UNICODECHAR(uchar VARCHAR2) 
RETURN VARCHAR2 DETERMINISTIC IS

    utf16 VARCHAR2(10) := ASCIISTR(uchar);
    sg1 VARCHAR2(4);
    sg2 VARCHAR2(4);
    codepoint INTEGER;
BEGIN
    IF REGEXP_LIKE(utf16, '^\\') THEN
        IF LENGTH(utf16) = 5 THEN
            RETURN REGEXP_REPLACE(utf16, '^\\');
        ELSE
            sg1 := REGEXP_SUBSTR(utf16, '[[:xdigit:]]{4}');
            sg2 := REGEXP_SUBSTR(utf16, '[[:xdigit:]]{4}', 5);
            codepoint := 2**10 * (TO_NUMBER(sg1, 'XXXX') - TO_NUMBER('D800', 'XXXX')) + TO_NUMBER(sg2, 'XXXX') - TO_NUMBER('DC00', 'XXXX') + 2**16;
            RETURN TO_CHAR(codepoint, 'fmXXXXXX');
        END IF; 
    ELSE
        RETURN TO_CHAR(ASCII(uchar), 'fmXX');
    END IF;

END UNICODECHAR;

Затем выполните

select TO_NUMBER(UNICODECHAR(sym), 'XXXXXXXX') from ..
...