Простой сопоставитель регулярных выражений — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
м (rollbackEdits.php mass rollback)
 
Строка 1: Строка 1:
{| class="wikitable" align="center" style="color: red; background-color: black; font-size: 56px; width: 800px;"
 
|+
 
|-align="center"
 
|'''НЕТ ВОЙНЕ'''
 
|-style="font-size: 16px;"
 
|
 
24 февраля 2022 года российское руководство во главе с Владимиром Путиным развязало агрессивную войну против Украины. В глазах всего мира это военное преступление совершено от лица всей страны, всех россиян.
 
 
Будучи гражданами Российской Федерации, мы против своей воли оказались ответственными за нарушение международного права, военное вторжение и массовую гибель людей. Чудовищность совершенного преступления не оставляет возможности промолчать или ограничиться пассивным несогласием.
 
 
Мы убеждены в абсолютной ценности человеческой жизни, в незыблемости прав и свобод личности. Режим Путина — угроза этим ценностям. Наша задача — обьединить все силы для сопротивления ей.
 
 
Эту войну начали не россияне, а обезумевший диктатор. И наш гражданский долг — сделать всё, чтобы её остановить.
 
 
''Антивоенный комитет России''
 
|-style="font-size: 16px;"
 
|Распространяйте правду о текущих событиях, оберегайте от пропаганды своих друзей и близких. Изменение общественного восприятия войны - ключ к её завершению.
 
|-style="font-size: 16px;"
 
|[https://meduza.io/ meduza.io], [https://www.youtube.com/c/popularpolitics/videos Популярная политика], [https://novayagazeta.ru/ Новая газета], [https://zona.media/ zona.media], [https://www.youtube.com/c/MackNack/videos Майкл Наки].
 
|}
 
 
 
{{Задача
 
{{Задача
 
|definition =
 
|definition =

Текущая версия на 19:11, 4 сентября 2022

Задача:
Даны регулярное выражение и текст. Нужно проверить допускает ли регулярное выражение данный текст.


Алгоритм

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

[math]c[/math] — один любой буквенный символ,
[math]\ldotp[/math] — один любой символ,
[math]\wedge[/math] — символ начала текста,
[math]$[/math] — символ конца текста,
[math]*[/math] — предыдущий символ встречается ноль или более раз.

Например, для [math]\mathtt{http://\ldotp*wiki\ldotp*com}[/math], очевидно, проще написать простой сопоставитель, чем строить НКА.

Псевдокод

// поиск вхождения регулярного выражения в любом месте текста
function match(regexp: String, text: String): boolean  
    if regexp[0] == '^'
        return matchHere(regexp[1:], text)  // regexp[n:] возвращает regexp без первых n элементов за O(1)
    int i = 0
    while i [math]\leqslant[/math] text.length
        if matchHere(regexp, text[i:])
            return true
        i++
    return false

Функция [math]\mathrm{match(regexp, text)}[/math] проверяет есть ли вхождение регулярного выражения в любом месте в пределах текста. Если существует более одного вхождения, то найдется самое левое и самое короткое.

Логика функции [math]\mathrm{match}[/math] проста. Если [math]\wedge[/math] — первый символ регулярного выражения, то любое возможное вхождение должно начинаться в начале текста. То есть если [math]\wedge[/math][math]abc[/math] — регулярное выражение, то [math]abc[/math] должно входить в текст только с первой позиции текста, а не где-то в середине текста. Это проверяется путем сопоставления остатка регулярного выражения с текстом, начиная с первой позиции и нигде более.

В противном случае регулярное выражение может входить в текст в любой позиции. Это проверяется путем сопоставления регулярного выражения во всех позициях текста. Если регулярное выражение входит более одного раза в текст, то только самое левое вхождение будет распознано. То есть если [math]abc[/math] — регулярное выражение, то для него найдется самое левое вхождение в текст.

// поиск вхождения регулярного выражения в начале текста
function matchHere(regexp: String, text: String): boolean 
    if regexp[0] == '\0'
        return true 
    if regexp[1] == '*'  // не будет выхода за пределы строки, так как в конце regexp и text всегда есть символ '\0'
        return matchStar(regexp[0], regexp[2:], text)
    if regexp[0] == '$' and regexp[1] == '\0'
        return text == '\0'
    if text[0] != '\0' and (regexp[0] == '.' or regexp[0] == text[0])
        return matchHere(regexp[1:], text[1:])
    return false

Основная часть работы сделана в [math]\mathrm{matchHere(regexp, text)}[/math], которая сопоставляет регулярное выражение с текстом в текущей позиции. Функция [math]\mathrm{matchHere}[/math] пытается сопоставить первый символ регулярного выражения с первым символом текста. В случае успеха мы можем сравнить следующий символ регулярного выражения со следующим символом текста, вызвав [math]\mathrm{matchHere}[/math] рекурсивно. Иначе нет совпадения с регулярным выражением в текущей позиции текста.

// сопоставление с регулярным выражением вида: c*
function matchStar(c: char, regexp: String, text: String): boolean
    int i = 0
    while i [math]\leqslant[/math] text.length and (text[i] == c or c == '.')
        if matchHere(regexp, text[i:])
            return true
        i++
    return false

Рассмотрим возможные случаи:

  1. Если в ходе рекурсии регулярное выражение осталось пустым [math]\mathrm{(regexp[0] == \backslash0)},\,[/math] то текст допускается этим регулярным выражением.
  2. Если регулярное выражение имеет вид [math]c*[/math], то вызывается функция [math]\mathrm{mathchStar},\,[/math] которая пытается сопоставить повторение символа [math]c[/math], начиная с нуля повторений и увеличивая их количество, пока не найдет совпадение с оставшимся текстом. Если совпадение не будет найдено, то регулярное выражение не допускает текст. Текущая реализация ищет "кратчайшее совпадение", которое хорошо подходит для сопоставления с образцом, как в grep[1], где нужно как можно быстрее найти совпадение. "Наидлиннейшее совпадение" более интуитивно и больше подходит для текстовых редакторов, где найденное заменят на что-то. Большинство современных библиотек для работы с регулярными выражениями предоставляют оба варианта.
  3. Если регулярное выражение это [math]$[/math], то оно допускает этот текст тогда и только тогда, когда текст закончился.
  4. Если первый символ текста совпал с первым символом регулярного выражения, то нужно проверить совпадают ли следующий символ регулярного выражения со следующим символом текста, сделав рекурсивный вызов [math]\mathrm{matchHere}[/math].
  5. Если все предыдущие попытки найти совпадения провалились, то никакая подстрока из текста не допускается регулярным выражением.

Модификации

Немного изменим функцию [math]\mathrm{matchStar}[/math] для поиск самого левого и самого длинного вхождения [math]c*[/math]:

  1. Найдем максимальную последовательность подряд идущих символов [math]c[/math]. Назовем ее [math]S[/math].
  2. Сопоставим часть текста без [math]S[/math] с остатком регулярного выражения.
  3. Если части совпали, то текст допускается этим регулярным выражением. Иначе, если [math]S[/math] пусто, то текст не допускается этим регулярным выражением, иначе убираем один символ из [math]S[/math] и повторяем шаг [math]2[/math].

Псевдокод

function matchStar(c: char, regexp: String, text: String): boolean
    int i
    for (i = 0; text[i] != '\0' and (text[i] == c or c == '.'); i++)
    while i [math]\geqslant[/math] 0
        if matchHere(regexp, text[i:])
            return true
        i--
    return false

См. также

Примечания

Источники информации