Шел дождь. Иванов оглянулся и произнес: "Я пойду за зонтом. Выходи через полчаса."
В Oracle функции регулярных выражений слишком малофункциональны, что достаточно легко обходится с помощью хранимок на java - в java регулярные выражения значительнее функциональнее. Например, в оракловых регулярках нет look-around assertion, а в java - есть. Это и сподвигнуло меня написать пакет, который выполняет данные операции в java stored функциях.
UPD: Внес кое-какие изменения в текст пакетов, актуальные версии выложил на GitHub.UPD2: Добавил несколько вариантов разбиения строки на подстроки с помощью разделителя
Что этот пакет умеет:
function split_simple(p_str in varchar2,p_delim in varchar2)
return varchar2_table pipelined;
Это PL/SQL-ная функция, разбивающая не по регулярному выражению, а просто по заданной подстроке. Введена просто для удобства.
Данная функция разбивает по заданному регулярному выражению, где выражение и является разделителем.Первый параметр строка для разбиения, второй - паттерн регулярного выражения, третий - необязательный - максимальное количество частей, на которые нужно разбить.function split(pStr varchar2,pDelimRegexp varchar2,pMaxCount number default 0)
return varchar2_table;
Пример:
select * from table(
xt_regexp.split('Пожалуйста, сьешьте еще мягких французских булочек!','(?<=,)\s')
);
В примере разбивается по пробелам, перед которыми есть запятая.
Еще пример:
Разбиваем по пробелам с максимумом в 3 частиselect * from table(
xt_regexp.split('Пожалуйста, сьешьте еще мягких французских булочек!','\s',3)
);
function matches(pStr varchar2,pPattern varchar2,
pCANON_EQ number default 0,
pCASE_INSENSITIVE number default 0,
pCOMMENTS number default 0,
pDOTALL number default 0,
pMULTILINE number default 0,
pUNICODE_CAS number default 0,
pUNIX_LINES number default 0
)
return boolean;
Данная функция возвращает true, если в строке находится подстрока удовлетворяющая регулярному выражению. Первый параметр - строка, вторая - регулярка, остальные параметры - это модификаторы рег.выражения:
- CANON_EQ - каноническое равенство;
- CASE_INSENSITIVE - игнорирование регистра символов;
- COMMENTS - режим игнорирования пробелов с разрешением однострочных комментариев в шаблоне с символа #;
- DOTALL - подразумевать ли под точкой символ новой строки (по умолчанию нет);
- LITERAL - представлять шаблон как обычную строку, т.е. игнорировать значения спец. символов регулярных выражений как +;
- MULTILINE - многострочный режим;
- UNICODE_CASE - регстр букв с учетом юникода;
- UNIX_LINES - режим юниксовых строк, т.е. разделителем строк является только символ Rn
function get_matches(
pStr varchar2,
pPattern varchar2,
pMaxCount number default 0,
pCANON_EQ number default 0,
pCASE_INSENSITIVE number default 0,
pCOMMENTS number default 0,
pDOTALL number default 0,
pMULTILINE number default 0,
pUNICODE_CAS number default 0,
pUNIX_LINES number default 0)
return varchar2_table;
Функция возвращает коллекцию строк, удовлетворяющих регулярному выражению. Параметры аналогичны предыдущей функции, но добавлен еще необязательный параметр pMaxCount - максимальное кол-во возвращенных подстрок, если он не указан или равен нулю, то возвращаются все найденные.
Пример поиска слов с буквой "е":
select * from table(xt_regexp.get_matches('Пожалуйста, сьешьте еще мягких французских булочек!','\S*е\S*'));
Функция аналогична предыдущей - get_matches, но возвращает строку, состоящую из соединенных найденных подстрок через указанный в параметре разделитель.function join_matches(pStr varchar2,pPattern varchar2,pDelim varchar2 default ';',
pCANON_EQ number default 0,
pCASE_INSENSITIVE number default 0,
pCOMMENTS number default 0,
pDOTALL number default 0,
pMULTILINE number default 0,
pUNICODE_CAS number default 0,
pUNIX_LINES number default 0)
return varchar2;
function replace_all(pStr varchar2,pPattern varchar2,pReplacement varchar2)
return varchar2
Функция заменяет все подстроки удовлетворяющие регулярному выражению на строку указанную в третьем параметре.
Пример - заменяем запятые перед которыми слово "Пожалуйста" на "$1 Scott!!!!":
Результат:select xt_regexp.replace_all('Пожалуйста, сьешьте еще мягких французских булочек!','(?<=Пожалуйста)(,)','$1 Scott!!!!') from dual;
Пожалуйста, Scott!!!! сьешьте еще мягких французских булочек!
Данная функция полностью аналогична предыдущей, кроме того, что она заменяет только первое соответствие.function replace_first(pStr varchar2,pPattern varchar2,pReplacement varchar2)
return varchar2
UPD: Добавил функцию longest_overlap - возвращает максимальное совпадение в двух строках:
Пример: select xt_regexp.longest_overlap('1234567890','abcdef3456fgnk') from dual
Примеры использования:
Несколько вариантов разбиения строк на подстроки по разделителю:
1. С помощью регулярных выражений c 10g:
with t as ( select 'a,d,f,g,h,j,kassd,asdf,123,455,asd' str,',' delimiter from dual union all select '6|7|8|9' str,'|' delimiter from dual ) select * from t ,table( cast( multiset( select regexp_substr(t.str,'[^'||t.delimiter||',]+',1,level) -- с 10g from dual connect by regexp_substr(t.str,'[^'||t.delimiter||',]+',1,level) is not null -- c 10g -- для 11g лучше будет: -- level<=regexp_count(t.str,',') ) as sys.odcivarchar2list ) ) substring2. Для древних версий с помощью с substr+instr:
with t as ( select 'a,d,f,g,h,j,kassd,asdf,123,455,asd' str,',' delimiter from dual union all select '6|7|8|9' str,'|' delimiter from dual ) select t.* ,t2.* ,substr(t.str ,lag(t2.column_value,1,0)over(partition by t.str order by t2.column_value)+1 ,t2.column_value-lag(t2.column_value,1,0)over(partition by t.str order by t2.column_value)-1 ) from t ,table( cast( multiset( select instr(t.str||t.delimiter,t.delimiter,1,rownum) from all_objects where rownum<=1+length(t.str)-length(translate(t.str,'Z'||t.delimiter,'Z')) ) as sys.odcinumberlist ) ) t23. С помощью парсера xml(ненадежно):
with t as ( select 'a,d,f,g,h,j,kassd,asdf,123,455,asd' str,',' delimiter from dual union all select '6|7|8|9' str,'|' delimiter from dual ) select * from t ,xmltable( '/rows/*' passing xmltype('4. С помощью функций xml(ненадежно):') columns rn for ordinality ,subst varchar2(4000) path '.' ) t2 '||replace(t.str,t.delimiter,' ')||'
with t as ( select 'a,d,f,g,h,j,kassd,asdf,123,455,asd' str,',' delimiter from dual union all select '6|7|8|9' str,'\|' delimiter from dual ) select * from t ,xmltable( ('for $r in ora:tokenize(.,"'||t.delimiter||'") return{$r} ') passing t.str columns subst varchar2(4000) path '.' )
Comments
Спасибо за статью, Саян!
В коде для 11g есть небольшая ошибка, не хватает "+1" к кол-ву символов:
-- для 11g лучше будет:
-- level<=regexp_count(t.str,',') + 1
при 2х элементах будет один разделитель, поэтому получится level <= 1 и последний элемент будет исключен. То же верно и для случаев 2+ элементов.
Отправить комментарий