Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Регулярное выражение для поиска непоследовательных дубликатов слов

#sql #регулярные_выражения #oracle


([[a-zа-я|А-Я]+)([[:space:]]+)\1


Данное регулярное выражение позволяет найти в тексте дубликаты слов, которые следует
друг за другом. Например:
"Что мне мне делать".
Но каким должно быть регулярное выражение, если я хочу найти непоследовательно повторяющиеся
слова?
    


Ответы

Ответ 1



Можно воспользоваться следующим выражением: SELECT * FROM regexTest WHERE REGEXP_LIKE( test, '([^_a-zA-Z0-9а-яА-ЯёЁ]|^)([a-zA-Zа-яА-ЯёЁ]+)[^_a-zA-Z0-9а-яА-ЯёЁ].*\2', 'i') См. демо регулярного выражения. Подробности ([^_a-zA-Z0-9а-яА-ЯёЁ]|^) - Захватываемая подмаска №1 (в регулярках Oracle нет поддержки незахватываемых групп): любой 1 символ, отличный от английских и русских букв, цифр и знака подчёркивания ([^_a-zA-Z0-9а-яА-ЯёЁ]) или (|) начало строки (^) ([a-zA-Zа-яА-ЯёЁ]+) - Захватываемая подмаска №2: 1 и более английских или русских букв [^_a-zA-Z0-9а-яА-ЯёЁ] - любой 1 символ, отличный от английских и русских букв, цифр и знака подчёркования .* - 0 и более любых символов, отличных от знака перевода на новую строку (если использовать модификатор n (т.е. третий аргумент должен быть 'in'), можно найти повторяющиеся слова на разных строчках строки) \2 - обратная ссылка на значение захватываемой подмаски №2. См. SQL-демо: CREATE TABLE regexTest( test varchar2(50)); INSERT INTO regexTest VALUES ('Что мне тут мне делать'); INSERT INTO regexTest VALUES ('Что мне мне делать'); INSERT INTO regexTest VALUES ('Что тут мне делать'); INSERT INTO regexTest VALUES ('Что делать? Дела?'); INSERT INTO regexTest VALUES ('Дела,дела...'); INSERT INTO regexTest VALUES ('Ничего-ничего, так себе...'); COMMIT; SELECT * FROM regexTest WHERE REGEXP_LIKE(test, '([^_a-zA-Z0-9а-яА-ЯёЁ]|^)([a-zA-Zа-яА-ЯёЁ]+)[^_a-zA-Z0-9а-яА-ЯёЁ].*\2') Результат: TEST Что мне тут мне делать Что мне мне делать Дела,дела... Ничего-ничего, так себе... Флаг 'i' позволяет находить повторяющиеся слова в разных регистрах. Если его убрать, будут найдены только 2 результата: Что мне тут мне делать Что мне мне делать

Ответ 2



Так найти только первое совпадение (см. демо регулярного выражения): select regexp_substr (str, pattern) match from ( select 'не как то что мне не мне что' str, '(^|\s)(\w+)\s.*\S+.*\s\2(\s|$)' pattern from dual ); match --------------------- не как то что мне не Если нужны все дубликаты, то самое простое, двигаться вперёд по словам (граница первого слова будет всегда начало строки - ^: select regexp_substr (substr(str, nextWordPos), pattern) match from ( select str, pattern, regexp_instr (str, '\w+', 1, level) nextWordPos from ( select 'не как то что мне не мне что' str, '^(\w+)\s.*\S+.*\s\1(\s|$)' pattern from dual ) connect by level < regexp_count (str, '\w+') -1 ) where regexp_substr (substr(str, nextWordPos), pattern) is not null ; match ------------------------------------------------- не как то что мне не что мне не мне что мне не мне Движок регулярных выражений Oracle имеет целый ряд ограничений, например, отсутствие границ слова (\b), нет поддержки незахватываемых групп, поиска вокруг и т.д. Если всё же надо обойти эти ограничения, то можно реализовать свою функцию, например, на Java. Пример (демо регулярного выражения): select column_value match from table (getMatches ( 'не как то что мне не мне что', '\b([\p{L}\p{N}_]+)\b(?=.*(\b\1\b))' )); Вывод: match ----------------------------------------- не at 0/2 has duplicate at 18/20 что at 10/13 has duplicate at 25/28 мне at 14/17 has duplicate at 21/24 Сама функция: create or replace type matchesType is table of varchar2(256); / create or replace and compile java source named "Utils" as import java.lang.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.List; import java.util.ArrayList; public class Match { public static java.sql.Array getMatches (String input, String pattern) throws java.sql.SQLException { return OraUtils.toSqlArray ("matchesType", getMatchesArray (input, pattern)); } private static String[] getMatchesArray (String input, String pattern) { List matches = new ArrayList(); Matcher m = Pattern.compile (pattern).matcher (input); while (m.find()) { matches.add (m.group()+" at "+m.start()+"/"+m.end ()+" has duplicate at "+m.start(2)+"/"+m.end(2)); } return matches.toArray (new String[0]); } } public class OraUtils { static java.sql.Array toSqlArray (String typeName, Object elements) throws java.sql.SQLException { oracle.jdbc.OracleDriver ora = new oracle.jdbc.OracleDriver(); oracle.jdbc.OracleConnection oraConn = (oracle.jdbc.OracleConnection) ora.defaultConnection(); java.sql.Array arr = oraConn.createARRAY(typeName.toUpperCase(), elements); return arr; } }; / create or replace function getMatches (s varchar2, p varchar2) return matchesType deterministic as language java name 'Match.getMatches (java.lang.String, java.lang.String) return java.sql.Array'; / Начиная с версии Java 7 (БД 12.1, 12.2) можно пользоваться \w+ для юникодов с флагом Pattern.UNICODE_CHARACTER_CLASS. Ожидать той же производительности, как и у функций внутреннего движка, не следует.

Комментариев нет:

Отправить комментарий