sql - oracle SQL字符串中ASCII 255的含义
问题描述
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)
对长度有何影响?
解决方案
连接字符串中的chr(255)
被视为空值,它没有长度,因此只计算其他非空字符 - 因此它得到 2 而不是 4。
ASCII 并没有真正达到 255,而且您并没有真正处理 ASCII。您的数据库字符集(大概)是 AL32UTF8,它是一个多字节字符集。来自FileFormat.Info 的摘要:
对于等于或小于 127(十六进制 0x7F)的任何字符,UTF-8 表示为一个字节。它只是完整 unicode 值的最低 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 形成的任何无效字符都被视为 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
推荐阅读
- r - 在 tibble 的不同级别应用函数
- firebase - React Native with Firebase:任何人都可以看到电子邮件格式不正确的地方吗?
- rdf - 如何使用任何 owl 本体映射大型数据文件
- python - sklearn learning_curve 和 StandardScaler
- django - 基类视图上的 Django URL 自定义参数
- sql - Oracle:将两行值合并为具有新列名的一行
- asp.net - Asp.net core 2.0部署海量dll
- python - 如何在 pandas df 上使用这个工作正则表达式(re)来删除多余的非数字字符,星号(*)?
- python - 为什么我会得到无效的 else 语法
- java - 在单个方法中重用连接对象