Значение ASCII 255 в оракуле SQL-строки - PullRequest
0 голосов
/ 21 сентября 2018
SELECT LENGTH('*'||CHR(255)||CHR(255)||'$')
FROM DUAL;

В этом запросе выводится 2 вместо 4?

Но

SELECT LENGTH(CHR(255)||CHR(255))
FROM DUAL;

Этот запрос дает вывод как null.Это означает, что строка, представленная CHR(255)||CHR(255), является пустой строкой длины 0.

Как это CHR(255) влияет на длину?

1 Ответ

0 голосов
/ 21 сентября 2018

chr(255) в вашей объединенной строке обрабатывается как ноль, который не имеет длины, поэтому учитываются только другие ненулевые символы - следовательно, он получает 2, а не 4.

ASCII недействительно поднимитесь до 255, и вы на самом деле не имеете дело с ASCII.Ваш набор символов базы данных (предположительно) AL32UTF8, который является многобайтовым набором символов.Из Резюме FileFormat.Info :

Для любого символа, равного или ниже 127 (шестнадцатеричный 0x7F), представление UTF-8 составляет один байт.Это всего лишь младшие 7 битов полного значения Юникода.Это также то же самое, что и значение ASCII.

Для символов, равных или ниже 2047 (шестнадцатеричный 0x07FF), представление UTF-8 разбивается на два байта.В первом байте будут установлены два старших бита, а третий бит сброшен (т. Е. От 0xC2 до 0xDF).У второго байта будет установлен верхний бит и очищен второй бит (т. Е. От 0x80 до 0xBF).

И из документации для chr():

Для многобайтовых наборов символов n должно преобразовываться в одну целую кодовую точку.Недопустимые кодовые точки не проверяются, и результат определения недопустимых кодовых точек является неопределенным.

Для UTF8 нет полной кодовой точки 255 / FF, поэтому chr(255) недопустимо.Фактически, в соответствии со спецификацией , нет никаких кодовых точек с октетом FF.

Вы могли бы ожидать, что оно будет отображено как 'ÿ';если вы используете кодировку, в которой она действительна, например AL16UTF16:

select chr(255 using nchar_cs), dump(chr(255 using nchar_cs), 1016) as chr_dump,
  unistr('\00ff'), dump(unistr('\00ff'), 1016) as unistr_dump
from dual;

C CHR_DUMP                                 U UNISTR_DUMP                                       
- ---------------------------------------- - --------------------------------------------------
ÿ Typ=1 Len=2 CharacterSet=AL16UTF16: 0,ff ÿ Typ=1 Len=2 CharacterSet=AL16UTF16: 0,ff          

, но из-за способа кодирования UTF8 это (и все, что выше 127) на самом деле является несколькими байтами, C3BF.

Здесь немного интереснее то, как Oracle обрабатывает этот недопустимый символ.Само по себе вы можете видеть, что он существует и является недействительным, но когда он объединяется с другим (действительным или недействительным) символом, он по существу игнорируется:

with t (descr, str) as (
  select 'chr(255)', chr(255) from dual
  union all select 'chr(255)||chr(255)', chr(255)||chr(255) from dual
  union all select q'['*'||chr(255)]', '*'||chr(255) from dual
  union all select q'[chr(255)||'$']', chr(255)||'$' from dual
  union all select q'['*'||chr(255)||'$']', '*'||chr(255)||'$' from dual
  union all select q'['*'||chr(255)||'$'||chr(255)]', '*'||chr(255)||'$'||chr(255) from dual
  union all select q'[chr(255)||'*'||chr(255)||'$']', chr(255)||'*'||chr(255)||'$' from dual
  union all select q'['*'||chr(255)||chr(255)||'$']', '*'||chr(255)||chr(255)||'$' from dual
  union all select q'['ÿ']', 'ÿ' from dual
  union all select 'chr(127)||chr(127)', chr(127)||chr(127) from dual
  union all select 'chr(127)||chr(128)', chr(127)||chr(128) from dual
  union all select 'chr(128)||chr(127)', chr(128)||chr(127) from dual
  union all select 'chr(128)||chr(128)', chr(128)||chr(128) from dual
)
select descr, str, dump(str, 1016) as str_dump, length(str) as str_length
from t;

DESCR                        ST STR_DUMP                                           STR_LENGTH
---------------------------- -- -------------------------------------------------- ----------
chr(255)                     ?  Typ=1 Len=1 CharacterSet=AL32UTF8: ff                       1
chr(255)||chr(255)              NULL                                                         
'*'||chr(255)                *  Typ=1 Len=1 CharacterSet=AL32UTF8: 2a                       1
chr(255)||'$'                $  Typ=1 Len=1 CharacterSet=AL32UTF8: 24                       1
'*'||chr(255)||'$'           *$ Typ=1 Len=2 CharacterSet=AL32UTF8: 2a,24                    2
'*'||chr(255)||'$'||chr(255) *$ Typ=1 Len=2 CharacterSet=AL32UTF8: 2a,24                    2
chr(255)||'*'||chr(255)||'$' *$ Typ=1 Len=2 CharacterSet=AL32UTF8: 2a,24                    2
'*'||chr(255)||chr(255)||'$' *$ Typ=1 Len=2 CharacterSet=AL32UTF8: 2a,24                    2
'ÿ'                          ÿ  Typ=1 Len=2 CharacterSet=AL32UTF8: c3,bf                    1
chr(127)||chr(127)            Typ=1 Len=2 CharacterSet=AL32UTF8: 7f,7f                    2
chr(127)||chr(128)             Typ=1 Len=1 CharacterSet=AL32UTF8: 7f                       1
chr(128)||chr(127)             Typ=1 Len=1 CharacterSet=AL32UTF8: 7f                       1
chr(128)||chr(128)              NULL                                                         

Последние несколько примеров показывают, что это не является конкретнымдо 255, это что-то выше 127, это проблема, потому что UTF8 переходит с 127 / 7F (все еще один байт) на 128 / C280 (два байта).(Вы можете увидеть скачок здесь , например.)

И вот короткая демонстрация того, что конкатенация любого недопустимого символа, сформированного с использованием 128-255, обрабатывается как нуль независимо от того, с чем он соединяется:

with t (n) as (
  select level from dual connect by level <= 255
)
select count(*), min(t1.n), max(t1.n), min(t1.n), max(t2.n)
from t t1
cross join t t2
where chr(t1.n)||chr(t2.n) is null
order by t1.n, t2.n;

  COUNT(*)  MIN(T1.N)  MAX(T1.N)  MIN(T1.N)  MAX(T2.N)
---------- ---------- ---------- ---------- ----------
     16384        128        255        128        255
...