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

Maj*_*ssi 5 regex oracle sqlplus

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

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

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

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

REG
------------
TEST 3304 V2
Run Code Online (Sandbox Code Playgroud)

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

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

REG
------------
TEST      V
Run Code Online (Sandbox Code Playgroud)

这些查询针对同一 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

Ale*_*ole 4

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

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
Run Code Online (Sandbox Code Playgroud)

冒着风险,因为您的个人资料显示您在摩洛哥,并且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
Run Code Online (Sandbox Code Playgroud)

原因是模式段被视为涵盖从到 的+-=所有字符的范围。在 ISO8859-1 和Windows 1252 字符集中,字符 43 到 61 以及落入该范围的所有数字(例如零是 48)都在该范围内,因此正则表达式会替换它们。在Windows 1256 字符集中也是如此。(以及任何基于 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
Run Code Online (Sandbox Code Playgroud)

也可以通过稍微修改范围来判断是范围问题;更改+-=+-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
Run Code Online (Sandbox Code Playgroud)

阅读有关语言排序的更多信息

不过,依赖 NLS 设置总是存在风险,因此最好通过更改模式以在开头或结尾使用连字符来完全避免范围问题,这样就根本不会将其视为范围;再次按照阿维纳什·拉吉的建议。