【问题标题】:The meaning of ASCII 255 in oracle SQL stringoracle SQL字符串中ASCII 255的含义
【发布时间】:2018-09-21 08:28:50
【问题描述】:
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) 对长度有何影响?

【问题讨论】:

    标签: sql string oracle character-encoding ascii


    【解决方案1】:

    连接字符串中的chr(255) 被视为空值,它没有长度,因此只计算其他非空字符 - 因此它得到 2 而不是 4。

    ASCII 并没有真正达到 255,而且您并没有真正处理 ASCII。您的数据库字符集(大概)是 AL32UTF8,它是一个多字节字符集。来自FileFormat.Info's summary

    对于任何等于或小于 127(十六进制 0x7F)的字符,UTF-8 表示为一个字节。它只是完整 unicode 值的最低 7 位。这也和 ASCII 值一样。

    对于等于或小于 2047(十六进制 0x07FF)的字符,UTF-8 表示分布在两个字节中。第一个字节将设置两个高位并清除第三位(即 0xC2 到 0xDF)。第二个字节将设置最高位并清除第二个位(即 0x80 到 0xBF)。

    从文档for chr():

    对于多字节字符集,n 必须解析为一个完整的代码点。无效码点不验证,指定无效码点的结果是不确定的。

    对于 UTF8,没有完整的代码点 255/FF,因此 chr(255) 无效。事实上,according to the spec,没有带 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(两个字节)。 (例如,您可以看到跳转here。)

    这里有一个快速演示,连接使用 128-255 形成的任何无效字符被视为 null,无论它与什么连接:

    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
    

    【讨论】:

      【解决方案2】:

      255 在 utf-8 编码中不是一个有效的隔离字节,但它确实是一个有效的 unicode 代码点。

      问题是 chr(255) 在 Oracle 中是什么意思?它是第 255 个 unicode 代码点吗?或者它是对 0x11111111 字节的引用。当然,这取决于所讨论的字符集。

      select
        length (chr(255) || chr(255)),  -- NULL
        lengthb(chr(255) || chr(255)),  -- NULL 
        length (nchr(255)||nchr(255)),  -- 2 valid unicode characters
        lengthb(nchr(255)||nchr(255))   -- 4 bytes (in AL32UTF8)
      from dual;
      

      要编写可移植的 sql 语句,我强烈建议仅在 (0..127) 上使用 CHR,并且永远不要在查询中编写 CHR(255)。当需要 unicode 字符时,坚持使用带有 CHR 的 ASCII 或移动到 NCHR。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-28
        • 2021-09-14
        • 2011-02-21
        • 1970-01-01
        • 2011-01-27
        • 2015-04-22
        相关资源
        最近更新 更多