IMO,此查询应返回A=1,B=2,
SELECT regexp_substr('A=1,B=2,C=3,', '.*B=.*?,') as A_and_B FROM dual
但它会返回整个字符串A=1,B=2,C=3,
。为什么?
更新1:
Oracle 10.2+需要在正则表达式中使用Perl风格的元字符。
更新2:
我的问题的一个更明确的形式(以避免有关Oracle版本和Perl风格regex扩展的可用性的问题(:
在同一系统上,为什么非贪婪量词有时能按预期工作,有时却不能?
这是正确的:
regexp_substr('A=1,B=2,C=3,', 'B=.*?,')
这不起作用:
regexp_substr('A=1,B=2,C=3,', '.*B=.*?,')
Fiddle
更新3:
是的,它似乎是一个bug。
Oracle支持部门对此问题的反应是什么
这个bug已经知道了吗?它有身份证吗?
这是BUG
您说得对,在Perl中,'A=1,B=2,C=3,' =~ /.*B=.*?,/; print $&
打印A=1,B=2,
您偶然发现的是Oracle Database 11g R2中仍然存在的一个错误。如果完全相同的正则表达式原子(包括量词但不包括贪婪修饰符(在正则表达式中出现两次,则无论第二次出现指定的贪婪如何,两次出现都将具有第一次出现所指示的贪婪。这些结果清楚地证明了这是一个错误(这里,"完全相同的正则表达式原子"是[^B]*
(:
SQL> SELECT regexp_substr('A=1,B=2,C=3,', '[^B]*B=[^Bx]*?,') as good FROM dual;
GOOD
--------
A=1,B=2,
SQL> SELECT regexp_substr('A=1,B=2,C=3,', '[^B]*B=[^B]*?,') as bad FROM dual;
BAD
-----------
A=1,B=2,C=3,
这两个正则表达式之间的唯一区别在于;"好";在第二个匹配列表中将"x"排除为可能的匹配项。由于"x"没有出现在目标字符串中,因此排除它应该没有什么区别,但正如您所看到的,删除"x"会产生很大的区别。那一定是个bug。
以下是Oracle 11.2中的更多示例:(SQL Fiddle还有更多示例(
SELECT regexp_substr('A=1,B=2,C=3,', '.*B=.*?,') FROM dual; => A=1,B=2,C=3,
SELECT regexp_substr('A=1,B=2,C=3,', '.*B=.*,') FROM dual; => A=1,B=2,C=3,
SELECT regexp_substr('A=1,B=2,C=3,', '.*?B=.*?,') FROM dual; => A=1,B=2,
SELECT regexp_substr('A=1,B=2,C=3,', '.*?B=.*,') FROM dual; => A=1,B=2,
-- Changing second operator from * to +
SELECT regexp_substr('A=1,B=2,C=3,', '.*B=.+?,') FROM dual; => A=1,B=2,
SELECT regexp_substr('A=1,B=2,C=3,', '.*B=.+,') FROM dual; => A=1,B=2,C=3,
SELECT regexp_substr('A=1,B=2,C=3,', '.+B=.+,') FROM dual; => A=1,B=2,C=3,
SELECT regexp_substr('A=1,B=2,C=3,', '.+?B=.+,') FROM dual; => A=1,B=2,
模式是一致的:第一次出现的贪婪被用于第二次出现,无论它是否应该出现。
看着反馈,我犹豫要不要加入,但我来了;-(
根据Oracle文档,*?和+?匹配"前面的子表达式"。对于*?特别是:
匹配前面的子表达式的零次或多次出现(nonreedyFootref 1(。尽可能匹配空字符串。
要创建子表达式组,请使用括号((:
将括号内的表达式视为一个单位。表达式可以是字符串或包含运算符的复杂表达式。
您可以在反向引用中引用子表达式。
这将允许您在同一个regexp中使用贪婪和非贪婪(实际上是多次交替(,并获得预期的结果。例如:
select regexp_substr('A=1,B=2,C=3,', '(.)*B=(.)*?,') from dual;
为了让这一点更清楚(我希望(,这个例子在同一个regexp_substr中使用贪婪和非贪婪,根据在哪里?被放置(它不仅仅对它看到的第一个子表达式使用规则(。还要注意,子表达式(\w(将只匹配字母数字和下划线,而不是@。
-- non-greedy followed by greedy
select regexp_substr('1_@_2_a_3_@_4_a', '(w)*?@(w)*') from dual;
结果:1_@_2_a_3_
-- greedy followed by non-greedy
select regexp_substr('1_@_2_a_3_@_4_a', '(w)*@(w)*?') from dual;
结果:1_@
你得到了一笔非常丰厚的赏金,所以我将尝试全面地解决它。
您在正则表达式处理中做出了不正确的假设。
- Oracle与Perl正则表达式不兼容兼容POSIX。它将其对Perl的支持描述为"受Perl影响">
- 如果您按照我的方式阅读该引用,Oracle就合法地选择了POSIX的用法
- 您对perl如何处理"*?"的描述不太正确
以下是我们讨论过的选项的混搭。这个问题的关键在于案例30
CASE SRC TEXT RE-FROM_WHOM结果---------------------------------------------------------------------1 Egor的原始源字符串A=1,B=2,C=3,.*B=.*?,Egor的原始模式"不起作用"A=1,B=2,C=3,2 Egor的原始源字符串A=1,B=2,C=3,.*B=。?,Egor的"工作正确"A=1,B=2,3 Egor的原始源字符串A=1,B=2,C=3,.*B=.+?,旧Pro评论1形式2 A=1,B=2,4 Egor的原始源字符串A=1,B=2,C=3+B=.*?,旧Pro评论1形式1 A=1,B=2,5 Egor的原始源字符串A=1,B=2,C=3,.*B=.{0,}?,旧Pro评论2 A=1,6 Egor的原始源字符串A=1,B=2,C=3,[^B]*B=[^Bx]*?,老专业答案形式1"好"A=1,B=2,7 Egor的原始源字符串A=1,B=2,C=3,[^B]*B=[^B]]*?,老专业答案形式2"坏"A=1,B=2,C=3,8 Egor原始源字符串A=1,B=2,C=3,(.(*B=(.(*?,TBone答案形式1 A=1、B=2,9 TBone答案示例21_@_2_a_3_@_4_a(\w(*@(\w(*TBone答案示例2表格11_@_2_a_3_10 TBone答案示例21_@_2_a_3_@_4_a(\w(*@(\w?TBone答案示例2表格2 1_@30 Egor原始源字符串A=1,B=2,C=3,.*B=(.(*?,Schemaczar变体强制Perl操作A=1,31 Egor的原始源字符串A=1,B=2,C=3,.*B=(.*(?,Egor的Schemaczar变体,强制POSIX A=1,B=2,C=3,32 Egor的原始源字符串A=1,B=2,C=3,.*B=.*{0,1}Schemaczar应用Egor的"非贪婪"A=1,B=2,C=3,33 Egor的原始源字符串A=1,B=2,C=3,.*B=(.(*{0,1}Schemaczar Egor的"非贪婪"A=1,B=2,C+3的另一个变体,
我很确定案例30就是你认为你在写的——也就是说,你认为"*?"比"*"本身有更强的关联。我想,对于Perl是正确的,但对于Oracle(以及可能是规范的POSIX(RE,"*?"的优先级和关联性低于"*"。因此,Oracle将其读作"(.*(?"(大小写31(,而Perl将其读为"(.(*?",即大小写30。
注意情况32和33表示"*{0,1}"与"*?"不同。
请注意,OracleREGEXP不像like那样工作,也就是说,它不需要匹配模式来覆盖整个测试字符串。使用"^"开始标记和"$"结束标记也可以帮助您做到这一点。
我的脚本:
SET SERVEROUTPUT ON
<<DISCREET_DROP>> begin
DBMS_OUTPUT.ENABLE;
for dropit in (select 'DROP TABLE ' || TABLE_NAME || ' CASCADE CONSTRAINTS' AS SYNT
FROM TABS WHERE TABLE_NAME IN ('TEST_PATS', 'TEST_STRINGS')
)
LOOP
DBMS_OUTPUT.PUT_LINE('Dropping via ' || dropit.synt);
execute immediate dropit.synt;
END LOOP;
END DISCREET_DROP;
/
--------------------------------------------------------
-- DDL for Table TEST_PATS
--------------------------------------------------------
CREATE TABLE TEST_PATS
( RE VARCHAR2(2000),
FROM_WHOM VARCHAR2(50),
PAT_GROUP VARCHAR2(50),
PAT_ORDER NUMBER(9,0)
) ;
/
--------------------------------------------------------
-- DDL for Table TEST_STRINGS
--------------------------------------------------------
CREATE TABLE TEST_STRINGS
( TEXT VARCHAR2(2000),
SRC VARCHAR2(200),
TEXT_GROUP VARCHAR2(50),
TEXT_ORDER NUMBER(9,0)
) ;
/
--------------------------------------------------------
-- DDL for View REGEXP_TESTER_V
--------------------------------------------------------
CREATE OR REPLACE FORCE VIEW REGEXP_TESTER_V (CASE_NUMBER, SRC, TEXT, RE, FROM_WHOM, RESULT) AS
select pat_order as case_number,
src, text, re, from_whom,
regexp_substr (text, re) as result
from test_pats full outer join test_strings on (text_group = pat_group)
order by pat_order, text_order;
/
REM INSERTING into TEST_PATS
SET DEFINE OFF;
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=.*?,','Egor''s original pattern "doesn''t work"','Egor',1);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=.?,','Egor''s "works correctly"','Egor',2);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=(.)*?,','Schemaczar Variant to force Perl operation','Egor',30);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=(.*)?,','Schemaczar Variant of Egor to force POSIX','Egor',31);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=.*{0,1}','Schemaczar Applying Egor''s ''non-greedy''','Egor',32);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=(.)*{0,1}','Schemaczar Another variant of Egor''s "non-greedy"','Egor',33);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('[^B]*B=[^Bx]*?,','Old Pro answer form 1 "good"','Egor',6);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('[^B]*B=[^B]*?,','Old Pro answer form 2 "bad"','Egor',7);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=.+?,','Old Pro comment 1 form 2','Egor',3);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.*B=.{0,}?,','Old Pro comment 2','Egor',5);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('.+B=.*?,','Old Pro comment 1 form 1','Egor',4);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('(.)*B=(.)*?,','TBone answer form 1','Egor',8);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('(w)*?@(w)*','TBone answer example 2 form 1','TBone',9);
Insert into TEST_PATS (RE,FROM_WHOM,PAT_GROUP,PAT_ORDER) values ('(w)*@(w)*?','TBone answer example 2 form 2','TBone',10);
REM INSERTING into TEST_STRINGS
SET DEFINE OFF;
Insert into TEST_STRINGS (TEXT,SRC,TEXT_GROUP,TEXT_ORDER) values ('A=1,B=2,C=3,','Egor''s original source string','Egor',1);
Insert into TEST_STRINGS (TEXT,SRC,TEXT_GROUP,TEXT_ORDER) values ('1_@_2_a_3_@_4_a','TBone answer example 2','TBone',2);
COLUMN SRC FORMAT A50 WORD_WRAP
COLUMN TEXT FORMAT A50 WORD_WRAP
COLUMN RE FORMAT A50 WORD_WRAP
COLUMN FROM_WHOM FORMAT A50 WORD_WRAP
COLUMN RESULT FORMAT A50 WORD_WRAP
SELECT * FROM REGEXP_TESTER_V;
因为您选择了太多:
SELECT
regexp_substr(
'A=1,B=2,C=3,',
'.*?B=.*?,'
) as A_and_B, -- Now works as expected
regexp_substr(
'A=1,B=2,C=3,',
'B=.*?,'
) as B_only -- works just fine
FROM dual
SQL Fiddle:http://www.sqlfiddle.com/#!4/d41d8/11450