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,
。为什么?
UPD: Oracle 10.2+需要在正则表达式中使用Perl样式的元字符。
UPD2:
更明确的问题形式(避免有关Oracle版本和Perl样式正则表达式扩展的可用性的问题):
为什么在同一系统中非贪婪量词有时会按预期工作,有时却不能?
这是正常的:
regexp_substr('A=1,B=2,C=3,', 'B=.*?,')
这不起作用:
regexp_substr('A=1,B=2,C=3,', '.*B=.*?,')
UPD3:
是的,这似乎是一个错误
任何人都可以就此问题提供Oracle支持部门的反应吗?
这个虫子已经知道了吗?
它有ID吗?
答案 0 :(得分:24)
你是对的,在Perl中,'A=1,B=2,C=3,' =~ /.*B=.*?,/; print $&
打印A=1,B=2,
您偶然发现的是Oracle Database 11g R2中仍然存在的错误。如果完全相同的正则表达式运算符(不包括贪婪修饰符)在正则表达式中出现两次,则两次出现都将具有第一次出现所指示的贪婪,而不管第二次出现指定的贪婪程度。这些结果清楚地证明了这是一个错误:
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'会产生很大的不同。这必须是一个错误。
以下是Oracle 11.2中的更多示例:(SQL Fiddle with even more examples)
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,
模式是一致的:第一次出现的贪婪用于第二次出现是否应该。
答案 1 :(得分:6)
观察反馈,我犹豫是否跳进去,但我走了; - )
根据Oracle docs,*?和+?匹配“前面的子表达式”。对于*?具体是:
匹配前面子表达式的零次或多次出现 (nongreedyFootref 1)。尽可能匹配空字符串。
要创建子表达式组,请使用括号():
将括号内的表达式视为一个单元。表达方式 可以是包含运算符的字符串或复杂表达式。
您可以在后引用中引用子表达式。
这将允许您在同一个正则表达式中使用贪婪和非贪婪(实际上有许多交替时间),并具有预期结果。以你的例子:
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 _ @
答案 2 :(得分:1)
你有一个非常好的赏金,所以我将尝试全面地解决这个问题。
您在正则表达式处理中假设不正确。
这是我们讨论过的选项的混搭。这个问题的关键在于案例30
CASE SRC TEXT RE FROM_WHOM RESULT ------- ------------------------------- ------------------ ----------------- -------------------------------------------------- -------------- 1 Egor's original source string A=1,B=2,C=3, .*B=.*?, Egor's original pattern "doesn't work" A=1,B=2,C=3, 2 Egor's original source string A=1,B=2,C=3, .*B=.?, Egor's "works correctly" A=1,B=2, 3 Egor's original source string A=1,B=2,C=3, .*B=.+?, Old Pro comment 1 form 2 A=1,B=2, 4 Egor's original source string A=1,B=2,C=3, .+B=.*?, Old Pro comment 1 form 1 A=1,B=2, 5 Egor's original source string A=1,B=2,C=3, .*B=.{0,}?, Old Pro comment 2 A=1,B=2, 6 Egor's original source string A=1,B=2,C=3, [^B]*B=[^Bx]*?, Old Pro answer form 1 "good" A=1,B=2, 7 Egor's original source string A=1,B=2,C=3, [^B]*B=[^B]*?, Old Pro answer form 2 "bad" A=1,B=2,C=3, 8 Egor's original source string A=1,B=2,C=3, (.)*B=(.)*?, TBone answer form 1 A=1,B=2, 9 TBone answer example 2 1_@_2_a_3_@_4_a (\w)*?@(\w)* TBone answer example 2 form 1 1_@_2_a_3_ 10 TBone answer example 2 1_@_2_a_3_@_4_a (\w)*@(\w)*? TBone answer example 2 form 2 1_@ 30 Egor's original source string A=1,B=2,C=3, .*B=(.)*?, Schemaczar Variant to force Perl operation A=1,B=2, 31 Egor's original source string A=1,B=2,C=3, .*B=(.*)?, Schemaczar Variant of Egor to force POSIX A=1,B=2,C=3, 32 Egor's original source string A=1,B=2,C=3, .*B=.*{0,1} Schemaczar Applying Egor's 'non-greedy' A=1,B=2,C=3, 33 Egor's original source string A=1,B=2,C=3, .*B=(.)*{0,1} Schemaczar Another variant of Egor's "non-greedy" A=1,B=2,C=3,
我很确定CASE 30是你认为你写的 - 也就是说,你认为“*?”与“*”本身有更强的联系。对于Perl来说,我认为是真的,但对于Oracle(以及可能是规范的POSIX)RE,“*?”优先级和关联性低于“*”。所以Oracle将其读作“(。*)?” (案例31),而Perl将其读作“(。)*?”,即案例30。
注意案例32和33表明“* {0,1}”不像“*?”那样工作。
请注意,Oracle REGEXP不像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;
答案 3 :(得分:-1)
因为你选择了太多
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