Pourquoi un quantificateur non gourmand ne fonctionne pas dans Oracle regex?

OMI, cette requête doit retourner A=1,B=2,

SELECT regexp_substr('A=1,B=2,C=3,', '.*B=.*?,') as A_and_B FROM dual

Mais il renvoie ensemble de la chaîne A=1,B=2,C=3, à la place. Pourquoi?

UPD: Oracle 10.2+ requis pour utiliser des métacharactères de type Perl dans les expressions régulières.

UPD2:

Forme plus claire de ma question (pour éviter les questions sur la version Oracle et la disponibilité de L'extension regex Perl-style):

pourquoi sur le même système non gourmand quantifierfonctionne parfois comme prévu et parfois pas?

Cela fonctionne correctement:

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

Cela ne fonctionne pas:

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

tripoter

UPD3:

Oui, il semble être un bug.

quelqu'un Peut-il fournir le Support d'Oracle réaction sur cette question?

Est le bug est déjà connu?

A-t-elle une IDENTITÉ?

27
demandé sur Egor Skriptunoff 2013-05-23 02:30:39

5 réponses

C'est un BUG!

Vous avez raison, en Perl, 'A=1,B=2,C=3,' =~ /.*B=.*?,/; print $& imprime A=1,B=2,

ce sur quoi vous êtes tombé est un bogue qui existe toujours dans la base de données Oracle 11g R2. Si le même opérateur d'expression régulière (à l'exclusion du modificateur de greediness) apparaît deux fois dans une expression régulière, les deux occurrences auront la greediness indiquée par la première apparition indépendamment de la greediness spécifiée par la seconde. Que c'est un bug est clairement démontré par ces résultats:

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,

la seule différence entre les deux expressions régulières est que la "bonne" exclut 'x' comme correspondance possible dans la deuxième liste de correspondance. Puisque ' x 'n'apparaît pas dans la chaîne cible, l'exclusion ne devrait pas faire de différence, mais comme vous pouvez le voir, enlever le' x ' fait une grande différence. Que doit être un bug.

Voici d'autres exemples D'Oracle 11.2: (SQL jouer avec encore plus de exemples)

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,

la tendance est constante: la grêle de la première occurrence est utilisée pour la seconde occurrence, qu'elle le soit ou non.

23
répondu Old Pro 2013-05-27 03:17:49

en regardant le feedback, j'hésite à sauter dedans, mais me voilà; -)

Selon Oracle docs, le *? et +? correspond à une "sous-expression précédente". Pour *? plus précisément:

correspond à zéro ou plus occurrences de la précédente sous-expression (nongreedyFootref 1). Correspond à la chaîne vide dans la mesure du possible.

pour créer un groupe de sous-expressions, utilisez la parenthèse ():

Traite l'expression entre parenthèses comme une unité. Expression peut être une chaîne ou une expression complexe contenant des opérateurs.

vous pouvez faire référence à un sous-expression dans une référence à l'arrière.

ceci vous permettra d'utiliser des greedy et non-greedy (plusieurs fois en fait) dans le même regexp, avec des résultats attendus. Pour ton exemple:

select regexp_substr('A=1,B=2,C=3,', '(.)*B=(.)*?,') from dual;

pour rendre le point un peu plus clair (j'espère), cet exemple utilise gourmande et non-greedy dans le même regexp_substr, avec différents (corriger) les résultats selon l'endroit où l' ? est placé (il n'est PAS juste d'utiliser la règle pour la première sous-expression qu'il voit). Notez également que la sous-expression (\w) sera alphanumérique et soulignera seulement, pas @.

-- non-greedy followed by greedy 
select regexp_substr('1_@_2_a_3_@_4_a', '(\w)*?@(\w)*') from dual;

résultat: 1_@_2_a_3_

-- greedy followed by non-greedy
select regexp_substr('1_@_2_a_3_@_4_a', '(\w)*@(\w)*?') from dual;

résultat: 1_@

6
répondu tbone 2013-05-28 17:50:33

Vous avez une prime vraiment grande, donc je vais essayer de le clouer complètement.

vous faites des suppositions incorrectes dans la gestion de vos expressions régulières.

  1. Oracle N'est pas compatible avec les expressions régulières Perl, il est compatible avec POSIX. il décrit son soutien à Perl comme "Perl-Influenced"
  2. il y a un conflit de syntaxe intrinsèque autour de L'utilisation du Perl "*?"dans Oracle, si vous lire référence à la façon Je le fais, et Oracle choisit légitimement L'usage de POSIX
  3. votre description du comportement de perl "*?"pas tout à fait droit.

voici un aperçu des options dont nous avons discuté. La clé de cette question Est autour du cas 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,  

je suis assez sûr que L'affaire 30 est ce que vous pensiez écrire - c'est-à-dire, vous avez pensé le "*?"avait une association plus forte que le" * " à lui seul. Vrai pour Perl, je suppose, mais pour Oracle (et probablement canonique POSIX) RE, le"*"?"a une priorité et une associativité plus basses que"*". Donc, Oracle lit "(.*)?"(31) considérant que Perl lit "(.)*?"c'est, cas 30.

Note les cas 32 et 33 indiquent que" {0,1} "ne fonctionne pas comme"*?".

notez Qu'Oracle REGEXP ne fonctionne pas comme, c'est-à-dire qu'il n'a pas besoin du motif de correspondance pour couvrir toute la chaîne de test. L'utilisation des marqueurs " ^ "begin et" $ " end peut vous aider dans ce domaine également.

1
répondu Andrew Wolfe 2013-05-31 03:39:59

mon script:

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;
0
répondu Andrew Wolfe 2013-05-31 03:43:43

parce que votre sélection de trop

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 violon : http://www.sqlfiddle.com/#!4/d41d8/11450

-1
répondu exussum 2013-06-01 01:25:04