Как мне сделать сложное совпадение строк с Regexp? - PullRequest
1 голос
/ 05 ноября 2019

Я пытаюсь выполнить поиск шаблонов в MYSQL по некоторым неструктурированным текстовым полям на основе заметок сотрудников, которые различаются в зависимости от разных стилей ввода данных. Введенные данные могут записывать следующее для использования кофеина:

User 1: 'Caffeine: Never'
User 2: 'Caffeine - Not much'
User 3: 'Caffeine:  No' 
User 4: 'Caffeine-No'

Я пытаюсь найти способ поиска во всех записях, где написано «Кофеин» + (1 или более пробел ИЛИ без пробела) + (: ИЛИ- ИЛИ без символа) + (1 или более пробел ИЛИ без пробела) + (НЕ все, что начинается с 'N')

Я пробовал:

выбрать * из таблицы, где текст RLIKE[[: space:]] [[: punct:]] [[: space:]] * [^ nN] ';

Первая часть выражения работает,но я нахожу исключения в результате для последней части, исключая "n" или "N", и я не уверен, почему мой запрос не исключает. Я надеюсь, что это то, что кто-то может помочь прояснить здесь.

Ответы [ 2 ]

1 голос
/ 05 ноября 2019

Почему это происходит?

Вы по-прежнему соответствуете этим предложениям из-за возврата назад.

Поскольку вы используете [^nN], оно все равно может соответствовать blank или punct. Итак, вот что происходит (просто используя один из ваших примеров входных данных, чтобы проиллюстрировать это):

Используя Caffeine[:blank:]*[:punct:]*[:blank:]*[^nN] в качестве примера (все ваши шаблоны действуют одинаково).

User 4: 'Caffeine-No'
         ^^^^^^^^       matches Caffeine literally
                 ^      matches [:blank:] zero times
                 ^      matches [:punct:] one time
                  ^     matches [:blank:] zero times
                  ^     N doesn't match `[^nN]`, let's backtrack to see if something else works
                 ^      matches [:punct:] zero times
                 ^      matches [:blank:] zero times
                 ^      - matches [^nN]

Good match due to backtracking

В приведенном выше примере регулярное выражение позволяет [^nN] соответствовать символу -. Regex хочет, чтобы соответствовал чему-то, поэтому он исчерпает каждую возможность, пока не будет (или не останется и не останется).

Как это исправить?

Одним из способов решения этой проблемы является указание только возможных символов в позиции [^nN] (что-то вроде [0-9a-mo-z] и т. Д.), Но это может быстро усложниться. Лучшая альтернатива может быть следующей:

См. Работающий здесь SQL

select * from docs where content REGEXP 'Caffeine[[:blank:]]*[[:punct:]]*[[:blank:]]*[[:<:]][^nN]'

В приведенной выше строке используется [[:<:]], чтобы утверждать, что позиция является началомграница слова. Другие языки используют \b для обозначения того же. Это означает, что он гарантирует, что любой символ, кроме [0-9a-zA-Z_], соответствует слева от позиции, и что любой символ в [0-9a-zA-Z_] соответствует справа от позиции.

В других движках регулярных выражений то же самое может бытьлегко достигается с помощью притяжательных квантификаторов (обычно + после квантификатора, например .*+), но MySQL еще не имеет притяжательного токена (AFAIK).

0 голосов
/ 05 ноября 2019

Вместо того, чтобы пытаться угадать каждую возможную вариацию, вероятно, проще всего убрать "Кофеин" и все, что не является символом слова, с фронта и использовать оставшуюся часть. Используйте POSIX [:alnum:] (буквенно-цифровой) класс символов и отрицайте его.

select regexp_replace(answer, '^Caffeine[^[:alnum:]]+', '')
from quiz;

dbfiddle

Затем проанализируйте то, что осталось.

...