Страницы

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

понедельник, 9 декабря 2019 г.

Поиск дубликатов слова по маске

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


Как при помощи регулярного выражения найти повторяющиеся слова в пределах одного
предложения, либо предложение, имеющее повторяющиеся слова? Одна строка может содержать
несколько предложений.

Попытки реализации: 

select *
from   text_exp
where  regexp_like(text, '(.+)([^.!?])\1\2\1')


Этот вариант ближе: 

select distinct x.*, 
       regexp_substr(lower(text), '(\w+?)\s+(\S*\s)*?\1', level) sub, level
from   ezhus.text_exp x
connect by regexp_substr(lower(text), '(\w+?)\s+(\S*\s)*?\1', level) is not null
AND    PRIOR TEXT = TEXT 
AND    PRIOR dbms_random.value IS NOT NULL


Однако он захватывает еще лишние варианты, такие как края слов, например:

лава день ночь астра

Частично проблема решена. Следующий запрос выдает предложения, в которых слова повторяются
более 2х раз, за исключением предложений, в которых слова идут друг за другом: 

select distinct x.*,
       regexp_substr(lower(text), '[^[:alpha:]]([a-zа-я]+)[, :;]+.*\1[, :;]+.*\1[,
:;]*.*', level) sub,
       level
from   ezhus.text_exp x
connect by regexp_substr(lower(text), '[^[:alpha:]]([a-zа-я]+)[, :;]+\1[, :;]+\1[,
:;]*', level) is not null
AND    PRIOR TEXT = TEXT
AND    PRIOR dbms_random.value IS NOT NULL

    


Ответы

Ответ 1



Регулярные выражения в оракле слишком слабы, что бы одним выражением получить искомое. Ваш вариант можно допилить до частично рабочего результата - он максимум сможет находить одну пару слов, а если между ними были при этом другие слова - то их не найдет. Это возможно только с помощью забегающих вперед проверок, которых в POSIX регулярных выражениях, используемых Oracle, нет. Единственный путь который я вижу: сначала разбить текст на предложения. Затем, каждое предложение разбить на слова и дальше с этими словами работать обычными средствами SQL, т.е. группировкой и подсчетом количества. with TEXTs(TEXT) as( select 'text abc gef text nm. text this is test abc this abc.' from DUAL union all select 'this is row two of the test text, word "text" is double.' from DUAL ) select ID, pnum, word, p from ( select ID, p, pnum, regexp_substr(p, '\w+', 1, level) word from ( select ID, regexp_substr(text, '.*?(\.|$)', 1, level) p, level as pnum from (select lower(text) as text, rownum as ID from TEXTs) x connect by regexp_substr(text, '.*?(\.|$)', 1, level) is not null and PRIOR ID=ID and prior dbms_random.value is not null ) connect by PRIOR ID=ID and PRIOR pnum=pnum and prior dbms_random.value is not null and regexp_substr(p, '\w+', 1, level) is not null ) group by ID, p, pnum, word having count(1)>1 Результат: ID PNUM WORD P 1 1 text text abc gef text nm. 1 2 abc text this is test abc this abc. 1 2 this text this is test abc this abc. 2 1 is this is row two of the test text, word "text" is double. 2 1 text this is row two of the test text, word "text" is double.

Ответ 2



Решение двумя регулярками. Вторая учитывает тот случай, когда повторяющееся слово стоит первым предложении. Данное решение выводит колличество предложений, в которых содержится 3 и более повторяющихся слов: select x.*, regexp_count(lower(text), '[^[:alpha:]]([a-zа-я]+)[, :;]+[^!.?]*\1[, :;]+[^!.?]*\1')cnt, regexp_count(lower(text), '^([a-zа-я]+)[, :;]+[^!.?]*\1[, :;]+[^!.?]*\1[^[:alpha:]]')cnt1 from ezhus.text_exp x where regexp_count(lower(text), '[^[:alpha:]]([a-zа-я]+)[, :;]+[^!.?]*\1[, :;]+[^!.?]*\1') >= 1 OR regexp_count(lower(text), '^([a-zа-я]+)[, :;]+[^!.?]*\1[, :;]+[^!.?]*\1[^[:alpha:]]') >= 1 Еще один вариант: select x.* from ezhus.text_exp x where regexp_like(regexp_replace(lower(text), '^([0-9a-zа-я]+)[, :;]+[^!.?]*\1[, :;]+[^!.?]*\1', ' '||text), '[^[0-9a-zа-я]]*([0-9a-zа-я]+)[, :;]+[^!.?]*\1[, :;]+[^!.?]*\1') Просто выводит строки, где слово в предложении всречается более 2х раз

Ответ 3



Приведу пример своего кода, который вполне себе работает, учитывая то обстоятельство, что нужно найти три и более повторяющиеся слова в одном предложении. select * from( select id, regexp_replace(Z_3, '^', ' ')||' .' as Z_4 from( select id, regexp_replace(Z_2, '\s+',' ') as Z_3 from( select id, regexp_replace(Z, '[?!.\]',' .') as Z_2 from (select x.*, regexp_replace(slova, '[—\,;:]','') as Z from text x)y))) where regexp_like(Z_4, '(\s([а-я|\-]+)\s)([^\.]*)\1([^\.]*)\1([^\.]*)\.', 'i'); Идея заключается в том, что я убираю из основного текста разделители, оставляя только точки, каждое слово предложение ограничиваю двумя пробелами и переделываю многострочный текст в одну строку.

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

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