28

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:

正規表現で Perl スタイルのメタ文字を使用するには、Oracle 10.2+ が必要です。

更新 2:

私の質問のより明確な形式(OracleのバージョンとPerlスタイルの正規表現拡張機能の可用性に関する質問を避けるため):

同じシステムで、貪欲でない量指定子が期待どおりに機能する場合と機能しない場合があるのはなぜですか?

これは正しく動作します:

regexp_substr('A=1,B=2,C=3,', 'B=.*?,')

これは機能しません:

regexp_substr('A=1,B=2,C=3,', '.*B=.*?,')

フィドル

更新 3:

はい、バグのようです。

この問題に対するオラクルのサポートの反応は?

バグはすでに知られていますか?IDはありますか?

4

4 に答える 4

24

バグです!

あなたは正しいです、Perlでは'A=1,B=2,C=3,' =~ /.*B=.*?,/; print $&印刷しますA=1,B=2,

あなたが遭遇したのは、Oracle Database 11g R2 にまだ存在するバグです。まったく同じ正規表現アトム (量指定子を含み、貪欲修飾子を除く) が正規表現に 2 回出現する場合、2 回目の出現で指定された貪欲さに関係なく、最初の出現で示された貪欲さが両方の出現に適用されます。これがバグであることは、これらの結果によって明確に示されています (ここでは、「まったく同じ正規表現アトム」は です[^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,

2 つの正規表現の唯一の違いは、「適切な」正規表現では、2 番目の一致リストで「x」が一致の可能性として除外されることです。「x」は対象文字列に出現しないため、除外しても問題はありませんが、ご覧のとおり、「x」を削除すると大きな違いが生じます。それはバグでなければなりません。

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,

パターンは一貫しています。最初の出現の欲張りさは、2 番目の出現に使用されるかどうかに関係なく使用されます。

于 2013-05-26T06:12:48.810 に答える
7

フィードバックを見て、私は飛び込むのをためらっていますが、ここに行きます ;-)

Oracle docsによると、 *? +? 「前の部分式」に一致します。為に *?具体的には:

先行する部分式の 0 回以上の出現に一致します (nongreedyFootref 1)。可能な限り空の文字列に一致します。

部分式グループを作成するには、括弧 () を使用します。

括弧内の式を 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_@

于 2013-05-28T17:50:33.110 に答える
0

あなたは本当に素晴らしい報奨金を手に入れたので、私はそれを包括的に釘付けにしようとします.

正規表現の処理で間違った仮定をしています。

  1. Oracle は Perl 正規表現と互換性がなく、POSIX と互換性があります。Perlのサポートを「Perl-Influenced」と説明しています
  2. Perl の「*?」の使用に関して、固有の構文の競合があります。オラクルでは、私のやり方でそのリファレンスを読んだ場合、オラクルは合法的にPOSIXの使用法を選択します
  3. perl が "*?" をどのように処理するかについての説明 は正しくありません。

以下は、これまでに説明したオプションのマッシュアップです。この問題の鍵はケース 30 あたりにあります

    CASE SRC テキスト 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=.+?, Old Pro コメント 1 フォーム 2 A=1,B=2,      
          4 Egor の元のソース文字列 A=1,B=2,C=3, .+B=.*?, オールド プロ コメント 1 フォーム 1 A=1,B=2,      
          5 Egor の元のソース文字列 A=1,B=2,C=3, .*B=.{0,}?, Old Pro コメント 2 A=1,B=2,      
          6 Egor の元のソース文字列 A=1,B=2,C=3, [^B]*B=[^Bx]*?, Old Pro answer form 1 "good" A=1,B=2,      
          7 Egor の元のソース文字列 A=1,B=2,C=3, [^B]*B=[^B]*?, Old Pro answer form 2 "bad" A=1,B=2,C=3 、  
          8 Egor の元のソース文字列 A=1,B=2,C=3, (.)*B=(.)*?, TBone 回答フォーム 1 A=1,B=2,      
          9 TBone 回答例 2 1_@_2_a_3_@_4_a (\w)*?@(\w)* TBone 回答例 2 フォーム 1 1_@_2_a_3_    
         10 TBone回答例2 1_@_2_a_3_@_4_a (\w)*@(\w)*? TBone 回答例 2 フォーム 2 1_@           
         30 Egor の元のソース文字列 A=1,B=2,C=3, .*B=(.)*?, Perl 操作を強制する Schemaczar バリアント A=1,B=2,      
         31 Egor の元のソース文字列 A=1,B=2,C=3, .*B=(.*)?, POSIX A=1,B=2,C=3 を強制するための Egor のスキーマバリアント  
         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、  

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;
于 2013-05-31T03:39:59.703 に答える