带有连字符的 Oracle 正则表达式在 Windows 上的结果与在 Unix 上的结果不同

Posted

技术标签:

【中文标题】带有连字符的 Oracle 正则表达式在 Windows 上的结果与在 Unix 上的结果不同【英文标题】:Oracle regular expression having a hyphen doesn't give same result on Windows as on Unix 【发布时间】:2014-10-14 14:43:11 【问题描述】:

我有以下带有正则表达式的查询:

select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-=|;.:<>?,./]', ' ') as REG 
from dual;

当在 Windows 机器上通过 SQL*Plus 执行时,它会返回以下内容:

SQL>  select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-=|;.:<>?,./]', ' ') as REG from dual;

REG
------------
TEST 3304 V2

SunOS 机器上我得到不同的结果:

SQL>  select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-=|;.:<>?,./]', ' ') as REG from dual;

REG
------------
TEST      V

这些查询是针对同一个 Oracle 服务器运行的。输出的差异有什么原因吗?

Windows 上的 SQL*Plus 版本:

SQL*Plus: Release 11.2.0.1.0 Production on Mar. Oct. 14 15:36:35 2014

Copyright (c) 1982, 2010, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

Unix 上的 SQL*Plus 版本:

SQL*Plus: Release 11.2.0.2.0 Production on Tue Oct 14 16:01:26 2014

Copyright (c) 1982, 2010, Oracle.  All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

【问题讨论】:

如果您不希望它像范围运算符一样使用,请在 char 类的第一个或最后一个使用 - 这样[-`~!@#$%^&*()_+=|;.:?,./] 虽然行为不同,但仍然很奇怪。如果两个实例的版本和补丁级别完全相同,那么字符集是否不同? @AlexPoole 我在两台机器上都添加了 Oracle 和 SQL*Plus 的版本。如何检查字符集? @MajidL - 因为 Window 的客户端没有将数字视为范围的一部分(在ASCII 下,+-= 是从 chr(43) 到 chr(61) 的所有字符的范围),其中包括数字),那个环境中的NLS_LANG set to 是什么? 【参考方案1】:

正如 Avinash Raj 在 cmets 中所说,正则表达式模式中的连字符被解释为一个范围。该行为似乎取决于两个客户端使用的排序算法,该算法基于影响 NLS_SORT 值的 NLS_LANG 环境变量。

NLS_LANG=ENGLISH_UNITED KINGDOM.WE8ISO8859P1:

SQL> select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-=|;.:<>?,./]', ' ') as REG from dual;

REG
------------
TEST      V

SQL> select value from nls_session_parameters where parameter = 'NLS_SORT';

VALUE
----------
BINARY

你的个人资料显示你在摩洛哥,NLS_LANG="ARABIC_MOROCCO.AR8MSWIN1256"

SQL> select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-=|;.:<>?,./]', ' ') as REG from dual;

REG
------------
TEST 3304 V2

SQL> select value from nls_session_parameters where parameter = 'NLS_SORT';

VALUE
----------
ARABIC

原因是模式段+-= 被视为覆盖从+= 的所有字符的范围。在 ISO8859-1 和 Windows 1252 character set 中,即字符 43 到 61,并且所有数字都在该范围内 - 例如零是 48 - 在该范围内,因此正则表达式替换它们。 Windows 1256 character set 也是如此。 (以及任何基于 ASCII 的东西)。

但是您的 NLS_LANG 也在隐式更改排序顺序,并且它从 BINARY 切换到 ARABIC 排序会改变行为。您可以在单个会话中看到这一点; NLS_LANG=ENGLISH_UNITED KINGDOM.WE8ISO8859P1:

SQL> select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-=|;.:<>?,./]', ' ') as REG from dual;

REG
------------
TEST      V

SQL> alter session set NLS_SORT=ARABIC;

Session altered.

SQL> select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-=|;.:<>?,./]', ' ') as REG from dual;

REG
------------
TEST 3304 V2

您也可以通过稍微修改范围来判断这是一个范围问题;将+-= 更改为+-3,因此不包括更高的数字,但保持其他所有内容相同:

SQL> alter session set NLS_SORT=BINARY;

Session altered.

SQL> select REGEXP_REPLACE ('TEST 3304 V2', '[`~!@#$%^&*()_+-3|;.:<>?,./]', ' ') as REG from dual;

REG
------------
TEST    4 V

Read more about linguistic sorting.

不过,依赖 NLS 设置总是有风险的,因此最好通过将模式更改为在开头或结尾使用连字符来完全避免范围问题,这样就不会将其视为一个范围;再次按照 Avinash Raj 的建议。

【讨论】:

“更改排序顺序”是什么意思? Ascii 代码不是 ascii 吗? [ 不上课怎么样? 我推测这不是问题所在。 - 范围运算符是类内部的元字符,因此它是可逃避的,他似乎无法做到。 @sln - 也许但这不是一门课?你可以read more about linguistic sorting; BINARY 基于“字符编码方案定义的字符的数值”进行排序,而 ARABIC(或任何基于语言的排序)基于“反映每个字符正确语言顺序的数值”进行排序。因此,对于 ARABIC,+-= 范围恰好不包括数字,因为该方案是如何排序的。 @sln - 我添加了一个稍微修改过的查询,以表明这是一个范围问题;将 = 更改为 3 会消除 2 和 3 的出现,但现在会留下 4。 不正确,反斜杠是元字符,是类中的主要字符。它引入了序列构造以及转义元字符,特别是当破折号- 并且转义时可以在类中的任何位置。这就是问题出在其他地方的原因。更有可能是他们引擎中的一个极端错误。

以上是关于带有连字符的 Oracle 正则表达式在 Windows 上的结果与在 Unix 上的结果不同的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 使用带有 oracle regexp_substr 的正则表达式提取 json 字段

没有空格的 Oracle 正则表达式

ORACLE:如何使用 regexp_like 查找两个字符之间带有单引号的字符串?

Oracle正则表达式

Oracle正则表达式在特殊字符后显示字符串

oracle10g中,如何查询正则表达式匹配指定字符串的匹配个数?