programing

Oracle 정규식에서 탐욕스럽지 않은 정량자가 작동하지 않는 이유는 무엇입니까?

javamemo 2023. 6. 9. 21:41
반응형

Oracle 정규식에서 탐욕스럽지 않은 정량자가 작동하지 않는 이유는 무엇입니까?

이는 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:

네, 벌레인 것 같습니다.

이 문제에 대한 Oracle 지원 반응은 어떻습니까?

그 버그는 이미 알고 있습니까?신분증이 있습니까?

벌레야!

펄에서 당신이 옳습니다.'A=1,B=2,C=3,' =~ /.*B=.*?,/; print $&A=1,B=2,

우연히 발견한 것은 Oracle Database 11g R2에 여전히 존재하는 버그입니다.정규 표현식에서 정확히 동일한 정규 표현식 원자(양자를 포함하지만 탐욕 수식자를 제외함)가 두 번 나타나는 경우, 두 번의 경우 모두 두 번째 표현식에서 지정된 탐욕과 관계없이 첫 번째 표현식에 의해 표시되는 탐욕을 갖게 됩니다.은 이됩니다 ( " 동일한 원자는 이것이버여는것이증정명명게다됩니하확의원표자해현정과에규결기동서한러는그라히확한일은는여"정▁that▁is▁express원▁is(here▁(▁bug"자▁clearly▁these▁regular,▁results▁demonstrated").[^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'를 제거하면 차이가 큽니다.벌레인가 봐요.

다음은 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,

패턴은 일관성이 있습니다. 첫 번째 발생의 탐욕은 그래야 하는지 여부에 관계없이 두 번째 발생에 사용됩니다.

피드백을 보니 뛰어들기가 망설여지는데 갑니다 ;-)

오라클 문서에 따르면 *?와 +?는 "이전 하위 표현식"과 일치합니다.*?의 경우, 구체적으로:

이전 하위 표현식(nongreedyFootref 1)의 0개 이상의 항목과 일치합니다.가능할 때마다 빈 문자열과 일치합니다.

하위 표현식 그룹을 만들려면 괄호()를 사용합니다.

괄호 안의 식을 단위로 처리합니다.표현식은 연산자를 포함하는 문자열 또는 복합 표현식일 수 있습니다.

뒤 참조에서 하위 표현식을 참조할 수 있습니다.

이렇게 하면 동일한 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_@

당신은 정말 큰 현상금을 가지고 있어서 종합적으로 못 박으려고 합니다.

정규식 처리에서 잘못된 가정을 합니다.

  1. Oracle은 Perl 정규식과 호환되지 않으며 POSIX와 호환됩니다.Perl에 대한 지원을 "Perl-Influenced"라고 설명합니다.
  2. Oracle에서 Perl "*?"를 사용하는 것과 관련하여 본질적인 구문 충돌이 있습니다. 만약 당신이 제 방식대로 해당 참조를 읽는다면 Oracle은 POSIX 사용을 합법적으로 선택합니다.
  3. 펄이 "*?"를 처리하는 방법에 대한 설명이 정확하지 않습니다.

여기 우리가 논의한 옵션들의 매쉬업이 있습니다.이 문제의 핵심은 사례 30에 관한 것입니다.

CASE SRC 텍스트 REFROM_whom 결과------- ------------------------------- ------------------ ----------------- -------------------------------------------------- --------------1 에고르의 원래 소스 문자열 A=1,B=2,C=3, .*B=.*?, 에고르의 원래 패턴 "unnely't work" A=1,B=2,C=3,2 에고르의 원래 소스 문자열 A=1,B=2,C=3, .*B=.?, 에고르의 "정확히 작동하는" A=1,B=2,3 에고르의 원래 소스 문자열 A=1,B=2,C=3, .*B=.+?, Old Pro 주석 1 양식 2 A=1,B=2,4 에고르의 원래 소스 문자열 A=1,B=2,C=3, .+B=.*?, Old Pro 주석 1 형식 1 A=1,B=2,5 에고르의 원래 소스 문자열 A=1,B=2,C=3, .*B=.{0,}?, Old Pro 주석 2 A=1,B=2,6 에고르의 원래 소스 문자열 A=1,B=2,C=3,[^B]*B=[^Bx]*?, 이전 프로 답변 양식 1 "양호" A=1, B=2,7 에고르의 원래 소스 문자열 A=1,B=2,C=3,[^B]*B=[^B]*?, 이전 프로 답변 양식 2 "불량" A=1, B=2, C=3,8 에고르의 원래 소스 문자열 A=1,B=2,C=3,(.)*B=(.)*?, TBone 응답 양식 1 A=1,B=2,9 TB 원답 사례 21__2_a_3_@_4_a(\w)*?@(\w)* 원답 사례 2 양식 11_@_2_a_3_10TB 원답 사례 21_@_2_a_3_@_4_a(\w)*@(\w)*?TBone 답변 예시 2 폼 21_@30 에고르의 원래 소스 문자열 A=1,B=2,C=3, .*B=(.*?), Schemaczar Variant는 Perl 연산 A=1,B=2를 강제로 실행합니다.31 에고르의 원래 소스 문자열 A=1,B=2,C=3,.*B=sk*?, 에고르의 Schemaczar 변형이 POSIX A=1,B=2,C=3을 강제합니다.32 Egor의 원래 소스 문자열 A=1, B=2, C=3, .*B=.*{0,1} 에고르의 '비독점'을 적용하는 Schemaczar A=1,B=2,C=3,33 에고르의 원래 소스 문자열 A=1,B=2,C=3, .*B=(.)*{0,1} Schemaczar 에고르의 또 다른 변형인 "비정형" 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;

너무 많이 선택했기 때문에:

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

언급URL : https://stackoverflow.com/questions/16702672/why-doesnt-a-non-greedy-quantifier-sometimes-work-in-oracle-regex

반응형