Oracle REGEXP_INSTR() 和“a-z”字符范围与预期不匹配
Posted
技术标签:
【中文标题】Oracle REGEXP_INSTR() 和“a-z”字符范围与预期不匹配【英文标题】:Oracle REGEXP_INSTR() and "a-z" character range doesn't match as expected 【发布时间】:2019-09-18 07:02:37 【问题描述】:我想在 oracle 数据库中使用 REGEXP_INSTR()
来检查小写/大写字符。我知道[:upper:]
和[:lower:]
POSIX 字符类,但我选择了a-z
,这给了我非常奇怪的结果,我不明白。有人可以解释一下吗?
SELECT REGEXP_INSTR('abc','[A-Z]',1,1,0,'c') FROM DUAL
-- Got 2, expected 0
SELECT REGEXP_INSTR('zyx','[A-Z]',1,1,0,'c') FROM DUAL
-- Got 1, expected 0
SELECT REGEXP_INSTR('ABC','[a-z]',1,1,0,'c') FROM DUAL
-- Got 1, expected 0
SELECT REGEXP_INSTR('ZYX','[a-z]',1,1,0,'c') FROM DUAL
-- Got 2, expected 0
SELECT REGEXP_INSTR('a3','[A-F0-9]',1,1,0,'c') FROM DUAL
-- Got 2, expected 2
SELECT REGEXP_INSTR('b3','[A-F0-9]',1,1,0,'c') FROM DUAL
-- Got 1, expected 2
SELECT REGEXP_INSTR('b3','[A-F0-9]') FROM DUAL
-- Got 1, expected 1 or 2
SELECT REGEXP_INSTR('a3','[A-F0-9]') FROM DUAL
-- Got 2, expected same as above
【问题讨论】:
'c': Turn on case sensitive matching.
例如,REGEXP_INSTR('zyx','[A-Z]',1,1,0,'c')
将匹配z
,而你不能指望0
。请注意字符串中的第一个字符的位置为 1。 请参阅regular-expressions.info/oracle.html
是的,'c' 开启区分大小写的匹配。 'z' 是小写的。我希望 '[A-Z]' 匹配从 A 到 Z 的大写字符。为什么它会匹配 z?
请检查this post,检查NLS_COMP
和NLS_SORT
会话参数是否干扰正则表达式区分大小写匹配。
让我们假设c
使[A-Z]
等于[A-Za-z]
,我对此表示怀疑,因为这将区分大小写,为什么第一个查询不返回1
?
@xsrf 如果你设置NLS_SORT = binary
你会得到你预期的结果
【参考方案1】:
行为的原因是排序规则。见NLS_SORT
documentation:
如果值为 BINARY,则 ORDER BY 查询的排序顺序基于字符的数值(需要较少系统开销的二进制排序)。 如果值是命名语言排序,则排序基于定义的语言排序的顺序。 NLS_LANGUAGE 参数支持的大多数(但不是全部)语言也支持同名语言排序。
将NLS_SORT
设置为BINARY
,以便[A-Z]
可以按照与ASCII 表中相同的顺序进行解析,
alter session set nls_sort = 'BINARY'
然后,您将获得一致的结果。
请参阅online demo。
【讨论】:
【参考方案2】:好的,NLS_SORT
导致这种行为的答案是正确的,但我认为它不能以一种可以理解的方式解释它。我找到的所有文档都没有真正做到这一点......
您必须想象[a-z]
定义的字符范围实际上是从所有可能字符的单个子字符串派生的,这些字符根据NLS_SORT
进行排序。
让我们假设整个字母表只是字母数字字符。按BINARY
排序,这将产生一个类似0123456789abcdefgh...xyzABCDE...XYZ
的基本字符串。
由此派生,[0-6]
扩展到 [0123456]
、[a-f]
到 [abcdef]
、[5-b]
到 [56789ab]
等。
按linguistic_definition
排序,但是会产生不同的基本字符串,例如0123456789aAbBcCdDeF...xXyYzZ
。
由此派生,[0-6]
仍扩展为 [0123456]
,但 [a-f]
现在扩展为 [aAbBcCdDeEf]
和 [5-b]
扩展为 [56789aAb]
等...
这就是为什么a
不匹配[A-Z]
,但b
匹配。 [A-Z]
实际上扩展为 [AbBcC...yYzZ]
,其中包括 z
但不包括 a
。
实际上[A-Z]
甚至可能包含更多字符,例如[aAàáâÀÁÂ...]
等。
【讨论】:
以上是关于Oracle REGEXP_INSTR() 和“a-z”字符范围与预期不匹配的主要内容,如果未能解决你的问题,请参考以下文章
Oracle 正则表达式函数-REGEXP_INSTR 使用例子
Oracle正则表达式之 Regexp_substr,Regexp_instr,Regexp_replace,Regexp_like