web-dev-qa-db-fra.com

Fractionner la chaîne par espace et caractère tant que délimiteur dans Oracle avec regexp_substr

J'essaie de scinder une chaîne avec regexp_subtr, mais je ne peux pas le faire fonctionner.

Donc, d'abord, j'ai cette requête

select regexp_substr('Helloworld - test!' ,'[[:space:]]-[[:space:]]') from dual

qui extrait très joliment mon délimiteur - blank - blank

Mais ensuite, lorsque j'essaie de scinder la chaîne avec cette option, cela ne fonctionne tout simplement pas. 

select regexp_substr('Helloworld - test!' ,'[^[[:space:]]-[[:space:]]]+')from dual

La requête ne renvoie rien.

L'aide sera très appréciée! Merci

3
aleko_vp

SQL Fiddle

Configuration du schéma Oracle 11g R2 :

CREATE TABLE TEST( str ) AS
          SELECT 'Hello world - test-test! - test' FROM DUAL
UNION ALL SELECT 'Hello world2 - test2 - test-test2' FROM DUAL;

Requête 1 :

SELECT Str,
       COLUMN_VALUE AS Occurrence,
       REGEXP_SUBSTR( str ,'(.*?)([[:space:]]-[[:space:]]|$)', 1, COLUMN_VALUE, NULL, 1 ) AS split_value
FROM   TEST,
       TABLE(
         CAST(
           MULTISET(
             SELECT LEVEL
             FROM   DUAL
             CONNECT BY LEVEL < REGEXP_COUNT( str ,'(.*?)([[:space:]]-[[:space:]]|$)' )
           )
           AS SYS.ODCINUMBERLIST
         )
       )

Résultats:

|                               STR | OCCURRENCE |  SPLIT_VALUE |
|-----------------------------------|------------|--------------|
|   Hello world - test-test! - test |          1 |  Hello world |
|   Hello world - test-test! - test |          2 |   test-test! |
|   Hello world - test-test! - test |          3 |         test |
| Hello world2 - test2 - test-test2 |          1 | Hello world2 |
| Hello world2 - test2 - test-test2 |          2 |        test2 |
| Hello world2 - test2 - test-test2 |          3 |   test-test2 |
7
MT0

Essayer de nier la chaîne de correspondance '[[:space:]]-[[:space:]]' en la plaçant dans une classe de caractères avec un circumflexe (^) pour la nier ne fonctionnera pas. Tout ce qui se trouve entre deux crochets est traité comme une liste de caractères simples facultatifs, à l'exception des classes de caractères nommés nommés, qui se transforment en liste de caractères facultatifs. Toutefois, en raison de la manière dont les classes de caractères sont imbriquées, il est fort probable étant interprété comme suit:

  • [^[[:space:]] Un seul caractère entre crochets, sans espace ni espace
  • - suivi d'un seul trait d'union
  • [[:space:]] suivi d'un seul espace
  • ]+ suivi de 1 ou plusieurs crochets de fermeture.

Il peut être plus facile de convertir votre séparateur multi-caractères en un seul caractère avec regexp_replace, puis utilisez regex_substr pour trouver vos morceaux individuels:

select regexp_substr(regexp_replace('Helloworld - test!'
                                   ,'[[:space:]]-[[:space:]]'
                                   ,chr(11))
                    ,'([^'||chr(11)||']*)('||chr(11)||'|$)'
                    ,1 -- Start here
                    ,2 -- return 1st, 2nd, 3rd, etc. match
                    ,null
                    ,1 -- return 1st sub exp
                    )
  from dual;

Dans ce code, j'ai d'abord changé - en chr(11). C'est le caractère ASCII tabulation verticale (VT) qui est peu susceptible d'apparaître dans la plupart des chaînes de texte. Ensuite, l'expression correspondante de regexp_substr correspond à tous les caractères non VT suivis d'un caractère VT ou de la fin de la ligne. Seuls les caractères non VT sont renvoyés (la première sous-expression).

1
Sentinel

Si j'ai bien compris, cela vous aidera. Actuellement, vous obtenez une sortie sous la forme Helloworld (avec un espace à la fin). Donc, je suppose que vous ne voulez pas d’espace à la fin. Si c'est le cas, vous pouvez simplement utiliser l'espace dans le délimiteur.

select regexp_substr('Helloworld - test!' ,'[^ - ]+',1,1)from dual;

OUTPUT
Helloworld(No space at the end)

Comme vous l'avez mentionné dans votre commentaire, si vous voulez une sortie sur deux colonnes avec Helloworld et test!. vous pouvez faire ce qui suit.

select regexp_substr('Helloworld - test!' ,'[^ - ]+',1,1),
       regexp_substr('Helloworld - test!' ,'[^ - ]+',1,3) from dual;

OUTPUT
col1         col2
Helloworld   test!
0
Crazy2crack
CREATE OR REPLACE FUNCTION field(i_string            VARCHAR2
                                ,i_delimiter         VARCHAR2
                                ,i_occurance         NUMBER
                                ,i_return_number     NUMBER DEFAULT 0
                                ,i_replace_delimiter VARCHAR2) RETURN VARCHAR2     IS
  -----------------------------------------------------------------------
  -- Function Name.......: FIELD
  -- Author..............: Dan Simson
  -- Date................: 05/06/2016 
  -- Description.........: This function is similar to the one I used from 
  --                       long ago by Prime Computer.  You can easily
  --                       parse a delimited string.
  -- Example.............: 
  --  String.............: This is a cool function
  --  Delimiter..........: ' '
  --  Occurance..........: 2
  --  Return Number......: 3
  --  Replace Delimiter..: '/'
  --  Return Value.......: is/a/cool
  --------------------------------------------------------------------------    ---                                    
  v_return_string  VARCHAR2(32767);
  n_start          NUMBER := i_occurance;
  v_delimiter      VARCHAR2(1);
  n_return_number  NUMBER := i_return_number;
  n_max_delimiters NUMBER := regexp_count(i_string, i_delimiter);
BEGIN
  IF i_return_number > n_max_delimiters THEN
    n_return_number := n_max_delimiters + 1;
  END IF;
  FOR a IN 1 .. n_return_number LOOP
    v_return_string := v_return_string || v_delimiter || regexp_substr    (i_string, '[^' || i_delimiter || ']+', 1, n_start);
    n_start         := n_start + 1;
    v_delimiter     := nvl(i_replace_delimiter, i_delimiter);
  END LOOP;
  RETURN(v_return_string);
END field;


SELECT field('This is a cool function',' ',2,3,'/') FROM dual;

SELECT regexp_substr('This is a cool function', '[^ ]+', 1, 1) Word1
      ,regexp_substr('This is a cool function', '[^ ]+', 1, 2) Word2
      ,regexp_substr('This is a cool function', '[^ ]+', 1, 3) Word3
      ,regexp_substr('This is a cool function', '[^ ]+', 1, 4) Word4
      ,regexp_substr('This is a cool function', '[^ ]+', 1, 5) Word5
  FROM dual;
0
Dan

Légère amélioration de la réponse de MT0. Dynamic compte en utilisant regexp_count et prouve qu'il gère les valeurs NULL où le format de [^ délimiteur] + en tant que modèle ne gère PAS les éléments de liste NULL. Plus d'informations à ce sujet ici: Séparez les valeurs en colonnes par des virgules

SQL> with tbl(str) as (
  2    select ' - Hello world - test-test! -  - test - ' from dual
  3  )
  4  SELECT LEVEL AS Occurrence,
  5         REGEXP_SUBSTR( str ,'(.*?)([[:space:]]-[[:space:]]|$)', 1, LEVEL, NULL, 1 ) AS split_value
  6  FROM   tbl
  7  CONNECT BY LEVEL <= regexp_count(str, '[[:space:]]-[[:space:]]')+1;

OCCURRENCE SPLIT_VALUE
---------- ----------------------------------------
         1
         2 Hello world
         3 test-test!
         4
         5 test
         6

6 rows selected.

SQL>
0
Gary_W