PL / SQL регулярные выражения соответствуют запятым, а не внутри кавычек - PullRequest
2 голосов
/ 05 февраля 2020

У меня есть строка, разделенная запятыми, и мне нужно сопоставить все запятые в этой строке, кроме запятых внутри двойных кавычек. Для этого я использую регулярное выражение.

,,,,"8000000,B767-200","B767-200","Boeing 767-200","ACFT",,,,,,,,,,,,,,,,,,,,,,,,,,

Я пробовал следующие шаблоны регулярных выражений, но ни один из них не работает в PL / SQL, но работает в онлайн-тестерах регулярных выражений.

,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))

(?!\B"[^"]*),(?![^"]*"\B)

Я использую функцию REGEXP_INSTR внутри процедуры в PL / SQL, чтобы определить индекс запятых. Может кто-нибудь предложить мне рабочий шаблон регулярных выражений в PL / SQL для этой цели или помочь мне написать один.

Спасибо.

Ответы [ 2 ]

3 голосов
/ 05 февраля 2020

Oracle не поддерживает группы предварительного просмотра и группы без захвата, поэтому вам нужно будет сопоставить кавычки.

Предполагая, что вы можете иметь строку без кавычек или строку в кавычках (которая может содержать экранированные кавычки), затем вы можете использовать регулярное выражение:

([^",]*|"(\\"|[^"])*"),

которое вы можете использовать следующим образом:

WITH matches ( id, csv, start_pos, comma_pos, idx,  num_matches ) AS (
  SELECT id,
         csv,
         1,
         REGEXP_INSTR( csv, '([^",]*|"(\\"|[^"])*"),', 1, 1, 1, NULL ) - 1,
         1,
         REGEXP_COUNT( csv, '([^",]*|"(\\"|[^"])*"),' )
  FROM   test_data
UNION ALL
  SELECT id,
         csv,
         REGEXP_INSTR( csv, '([^",]*|"(\\"|[^"])*"),', 1, idx + 1, 0, NULL ),
         REGEXP_INSTR( csv, '([^",]*|"(\\"|[^"])*"),', 1, idx + 1, 1, NULL ) - 1,
         idx + 1,
         num_matches
  FROM   matches
  WHERE  idx < num_matches
)
SELECT id,
       idx,
       start_pos,
       comma_pos,
       SUBSTR( csv, start_pos, comma_pos - start_pos ) AS value
FROM   matches

так для ваших тестовых данных:

CREATE TABLE test_data ( id, csv ) AS
SELECT 1, ',,,,"8000000,B767-200","B767-200","Boeing 767-200","ACFT",,,,,,,,,,,,,,,,,,,,,,,,,,' FROM DUAL

какие выходы:

ID | IDX | START_POS | COMMA_POS | VALUE             
-: | --: | --------: | --------: | :-----------------
 1 |   1 |         1 |         1 | <em>null</em>              
 1 |   2 |         2 |         2 | <em>null</em>              
 1 |   3 |         3 |         3 | <em>null</em>              
 1 |   4 |         4 |         4 | <em>null</em>              
 1 |   5 |         5 |        23 | "8000000,B767-200"
 1 |   6 |        24 |        34 | "B767-200"        
 1 |   7 |        35 |        51 | "Boeing 767-200"  
 1 |   8 |        52 |        58 | "ACFT"            
 1 |   9 |        59 |        59 | <em>null</em>              
 1 |  10 |        60 |        60 | <em>null</em>              
 1 |  11 |        61 |        61 | <em>null</em>              
 1 |  12 |        62 |        62 | <em>null</em>              
 1 |  13 |        63 |        63 | <em>null</em>              
 1 |  14 |        64 |        64 | <em>null</em>              
 1 |  15 |        65 |        65 | <em>null</em>              
 1 |  16 |        66 |        66 | <em>null</em>              
 1 |  17 |        67 |        67 | <em>null</em>              
 1 |  18 |        68 |        68 | <em>null</em>              
 1 |  19 |        69 |        69 | <em>null</em>              
 1 |  20 |        70 |        70 | <em>null</em>              
 1 |  21 |        71 |        71 | <em>null</em>              
 1 |  22 |        72 |        72 | <em>null</em>              
 1 |  23 |        73 |        73 | <em>null</em>              
 1 |  24 |        74 |        74 | <em>null</em>              
 1 |  25 |        75 |        75 | <em>null</em>              
 1 |  26 |        76 |        76 | <em>null</em>              
 1 |  27 |        77 |        77 | <em>null</em>              
 1 |  28 |        78 |        78 | <em>null</em>              
 1 |  29 |        79 |        79 | <em>null</em>              
 1 |  30 |        80 |        80 | <em>null</em>              
 1 |  31 |        81 |        81 | <em>null</em>              
 1 |  32 |        82 |        82 | <em>null</em>              
 1 |  33 |        83 |        83 | <em>null</em>              

дБ <> скрипка здесь

(Примечание: вы хотели сопоставьте запятые, и это регулярное выражение выполняет именно то, что вы запрашиваете, оно не соответствует ни одному конечному значению в списке, разделенном запятыми, так как нет запятой в конце. Если вы хотите это сделать, используйте регулярное выражение ([^",]*|"(\\"|[^"])*")(,|$) db <> fiddle .)

Если вы хотите это в процедуре, то:

CREATE PROCEDURE extract_csv_value(
  i_csv   IN VARCHAR2,
  i_index IN INTEGER,
  o_value OUT VARCHAR2
)
IS
BEGIN
  o_value := REGEXP_SUBSTR( i_csv, '([^",]*|"(\\"|[^"])*")(,|$)', 1, i_index, NULL, 1 );

  IF SUBSTR( o_value, 1, 1 ) = '"' THEN
    o_value := REPLACE( SUBSTR( o_value, 2, LENGTH( o_value ) - 2 ), '\"', '"' );
  END IF;
END;
/

затем:

DECLARE
  csv   VARCHAR2(4000) := ',,,,"8000000,B767-200","B767-200","Boeing 767-200","ACFT",,,,,,,,,,,,,,,,,,,,,,,,,,';
  value VARCHAR2(100);
BEGIN
  FOR i IN 1 .. 10 LOOP
    extract_csv_value( csv, i, value );
    DBMS_OUTPUT.PUT_LINE( LPAD( i, 2, ' ' ) || ' ' || value );
  END LOOP;
END;
/

выводит:

 1 
 2 
 3 
 4 
 5 8000000,B767-200
 6 B767-200
 7 Boeing 767-200
 8 ACFT
 9 
10 

db <> скрипка здесь

1 голос
/ 05 февраля 2020

Я пытался решить ее, не используя REGEX, поэтому проверьте следующую ПРОЦЕДУРУ, если она работает, как ожидалось.

CREATE OR REPLACE PROCEDURE p_extract(p_string IN VARCHAR) AS
    TYPE table_result IS TABLE OF VARCHAR2(255) INDEX BY PLS_INTEGER;
    t_retval table_result;

    opening BOOLEAN := FALSE;
    cnt INTEGER := 1;
    I INTEGER := 1;
    j INTEGER := 1;
BEGIN

    WHILE cnt <= LENGTH(p_string) AND cnt <> 0 LOOP
        IF substr(p_string, cnt, 1) = '"'THEN
            opening := NOT opening;
        END IF;

        IF opening THEN
            I := instr(p_string, '"', cnt + 1, 1);
            t_retval(t_retval.COUNT + 1) := substr(p_string, cnt, I - cnt + 1);
        END IF;

        cnt := instr(p_string, '"', cnt + 1, 1);
    END LOOP;

    FOR K IN t_retval.FIRST..t_retval.LAST LOOP
        dbms_output.put_line(t_retval(K));
    END LOOP;
END;

Проверьте это.

BEGIN
    p_extract(',,,,"8000000,B767-200","B767-200","Boeing 767-200","ACFT",,,,,,,,,,,,,,,,,,,,,,,,,,');
END;

--OUTPUT
/*
"8000000,B767-200"
"B767-200"
"Boeing 767-200"
"ACFT"
*/

Однако это не сработает, если вы пропустите последний или первый "

...