Как написать RegEx для извлечения N-го значения из CSV-подобной строки используя regexp_substr()
Как можно расширить следующее регулярное выражение (в POSIX стандарте), таким образом чтобы в качестве разделителя строк могли выступать значения ,, ;, /, , т.е. комбинации запятой, точки с запятой, слеша, пробелов и символа табуляции.
Пример исходных данных:
ID EMAIL
-- --------------------------------------------------
1 NULL
2 [email protected]
3 [email protected]; [email protected], [email protected]
4 [email protected], [email protected]
5 [email protected] [email protected]
В данном DB Fiddle я попытался извлечь второй email из каждой строки и хотел бы получить следующий результат (выходной датасет):
ID EMAIL
-- --------------------------------------------------
1 NULL
2 NULL
3 [email protected]
4 [email protected]
5 [email protected]
Т.е. в результате не должны присутствовать символы разделители: ,, ;, /,
Я попытался добавить POSIX значение [:space:] для того чтобы обрабатывать пробелы и знаки табуляции, но результат оказался неверным:
select
id,
regexp_substr (email, '(.*?)(([[:space:],;/])|($))', 1, 2, null, 1) as second_email
from tab;
ID EMAIL
-- --------------------------------------------------
1 NULL
2 NULL
3 NULL
4 NULL
5 [email protected]
Подскажите как исправить POSIX RegEx.
NOTE: я работаю с Oracle 12.2, который не понимает RegEx символы такие как \s, поэтому приходится использовать только регулярки, которые поддерживаются POSIX стандартом.
PS в данном примере я всегда извлекаю второе значение из строки - это упрощенный (минимальный воспроизводимый) пример. На самом деле это часть функции, которая должна уметь извлекать любое N-ое значение (N будет передаваться в функцию в качестве параметра).
Ответы (3 шт):
SELECT tab.id, jsontable.email_num, jsontable.email
FROM tab
CROSS JOIN JSON_TABLE('["' || REGEXP_REPLACE(tab.email, '[,;\/ ]+', '","') || '"]',
'$[*]' COLUMNS (email_num FOR ORDINALITY,
email VARCHAR(255) PATH '$')) jsontable;
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=182ab5331fc09c25ab534002c56c634f
Выглядит, что следующие регулярки работают:
select id, regexp_substr (
email, '(\w.*?)(([[:space:],;/])|($))', 1, 2, null, 1) as second_email
from tab
/
select id, regexp_substr (
email, '(.*?)(([[:space:],;/]+)|($))', 1, 2, null, 1) as second_email
from tab
Обе выводят:
ID SECOND_EMAIL
---------- --------------------------------------------------
1
2
3 [email protected]
4 [email protected]
Регулярное выражение (.*?)(([[:space:],;/])|($)) не всегда находит нужное совпадение, потому что (.*?) также находит пустую строку, а у вас в строках рядом с разделителями есть и пробельные символы, и [[:space:],;/] их и находит. Другими словами, вместо нужного значения (.*?) захватывает пустую строку, [[:space:],;/] находит пробел после, например, запятой, и возвращается совпадение.
Поэтому, вместо (.*?) нужно подставить такой шаблон, чтобы он не находил пустую строку, например
'([^[:space:]].*?)((' || '[[:space:],;/]' || ')|($))'
(Я использую оригинальный синтаксис со всеми подмасками, хотя я не уверен, нужны они здесь или нет.)
Шаблон ([^[:space:]].*?) не найдёт пустой строки, так как [^[:space:]] требует одного любого символа кроме пробельного, и именно с этого символа и начнётся совпадение.