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_COMPNLS_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

Oracle的REGEXP INSTR函数简单用法

oracle 如何截取两个"-"之间的字符串,如果是正则表达式该如何用啊?

ORACLE REGEXP应用实例

Oracle正则表达式-初级使用