LINUX.ORG.RU

Сравнение регулярных выражений

 


0

1

Есть одна база правил, которая тихонько выходит из-под контроля. Правила — это цепочка вида

- условие1
  действие
- условие2
  действие
- условие3
…

Цепочка просматривается сверху вниз, если правило подошло, выбирается действие и поиск завершается.

Правило это набор сопоставлений с образцом с помощью регулярных выражений метка1 =~ регулярка1, метка2 =~ регулярка2.

Всё вроде хорошо, но когда правил много и условия замысловатые, возникает риск ошибок.

Пример1:

- match: '{environment =~ ".*"}'
  action: discard
- match: '{name="SuperProduct"}
  action: pass

Второе правило никогда не сработает. Дополнительно, я обладаю некоей метаинформацией. В частности, окружения всегда именуются как Stage-Name-Region, где stage — dev|staging|prod, name — алфавитно-цифровые символы, region - доступные AWS’s regions. Также, известно, что все prod окружения находятся в европейских регионах. Поэтому

Пример2:

- match: '{environment =~ "prod.*"}'
  action: pass
- match: '{environment =~ ".*-eu-central.*"}
  action: pass

правила очевидно дублируют друг-друга и их можно упростить.

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

★★★★★
Ответ на: комментарий от kaldeon

Автоматически проверить базу правил на целостность, избыточность, возможность упрощения. Для этого нужно придумать как сравнивать регулярки. Примем, что regex1 > regex2, если всем образцам, которым соответствует regex2 также соответствует и regex1, а regex1 == regex2, если одновременно верно regex1 > regex2 и regex2 > regex1.

ugoday ★★★★★
() автор топика
Ответ на: комментарий от ugoday

Сравнение регулялок теоретически возможно реализовать.

Если в одном месте будет .*, а в другом .*(stage|stg).*, и оба значения должны содержать stage, то нужно сравнивать .* + stage = .*stage.* и .*(stage|stg).* + stage = .*stage.* (здесь в процессе может сгенерироваться длинное выражение, которое упрощается). Выражения эквивалентны. То есть мы превращаем выражение в такой вид, чтобы он учитывал возможные значения, указанные другим выражением. Похоже на то, что теоретически это тоже возможно.

Любой набор if (bool) return bool можно сократить в одно (большое и сложное) выражение при условии отсутствия промежуточного состояния. Можно сделать так и отправить в какой-нибудь сравниватель условных выражений. env!=foo && name==bar и name==bar && env!=foo эквивалентны.

В целом, для всего должны быть библиотеки, но вряд ли их будет много, вряд ли они будут написаны на одном языке и вряд ли они сразу заведуться в современном окружении. Про PCRE можно забыть. Про leftmost-first тоже.

Leftmost-first matching is difficult to explain.
When POSIX had to define the rules for regexps,
they chose to define them as leftmost-longest
even though all the implementations were leftmost-first,
because describing leftmost-first precisely was too
complex.

Всё это выглядит как довольно сложный, времязатратный проект для одного.

kaldeon
()
Последнее исправление: kaldeon (всего исправлений: 3)

По сути, не знаю, что ответить. Кажется, что овчинка выделки не стоит.

Но придерусь к мелочам. Сколько можно писать:

{environment =~ ".*-eu-central.*"}

Регекспы ищут подстроку. То же самое записывается короче и понятнее так:

{environment =~ "-eu-central"}

Заодно и быстрее работать будет (на микросекунды, но всё же).

- match: '{environment =~ ".*"}'

Скорее всего можно заменить на:

- match: '{environment =~ "."}'

И это по-моему, красивее, демонстрирует, что человек знает регекспы, и ещё эффективнее будет по памяти и CPU (на пикосекунды для коротких строк, но всё же).

Chiffchaff
()

Нормально конфигурацию задавать - если у тебя тупл stage-name-region, это должен быть тупл, а не склеенная строка. А так регулярки это КА, а КА это граф, а вхождение графа можно искать, только сомневаюсь что тебе это поможет. Если не хочешь делать нормально как написано выше, но всё равно будешь строки и регулярки конвертить в туплы, а туплы сравниваются и проверяются на вхождение тривиально.

Стоит добавить разве что что задача про регионы - отдельная, и в любом случае решается скармливанием списка всех известных регионов правилу. Правила выдающие одинаковый набор результатов эквивалентны.

anonymous
()
Ответ на: комментарий от ugoday

Мягко выражаясь, не всегда.

Да, есть нюансы. Но по умолчанию, интерфейс большинства регекспов в большинстве ЯП, с которыми я работал, ищет подстроку.

Таким образом, уродливые регекспы .*needle.* всегда эквивалентны более быстрым и элегантным needle.

Бессмысленные .* в начале и в конце строк чаще всего добавляют люди, которые не знают регекспы, а пытаются нашарить верное решение, глядя на чужие примеры, но не пытаясь их осмыслить.

Chiffchaff
()
Ответ на: комментарий от ugoday

Во-первых, нет, эвристики это всегда плохо, но я их и не предлагал, я предложил нормально структурировать конфиг.

И нет тут никакого общего решения, потому что всё завязано на твой формат строк окружений и списки возможных значений его частей.

anonymous
()
Ответ на: комментарий от ugoday

Я бы отложил захват мира. Есть задачи поближе

  1. Можно сначала договориться, что правила располагаются в определённом порядке
  • типа Allow, Deny
  • либо Deny, Allow

Или хотя бы сначала частные правила, а последним дефолт (пример 1)

  1. Ещё можно параллельно (или предварительно?) запускать другой обработчик, который проверит по всей базе, но не будет ничего применять. И куда-нибудь запишет результат проверки. А потом смотреть и много думать, если много совпадений больше чем по одному правилу

  2. ещё можно взять и ЗАПИСЫВАТЬ все входные данные. И потом на них составить тестовый набор, который прогонять по правилам, когда эти правила меняешь

router ★★★★★
()
Ответ на: комментарий от router

Можно сначала договориться,

Я как представляю со сколькими людьми придётся митинги митинговать, сердце в груди замирает.

прогонять по правилам, когда эти правила меняешь

Я же не один это делаю …

Пока что думаю о колхозном решении:

  1. Зная какие данные бывают написать генератор.

  2. Прогнать выхлоп генератора по базе правил, если где-то 0 срабатываний, значит у нас какое-то правило закрыто другими.

  3. Прогнать выхлоп по каждому правилу отдельно, если где-то одинаковое количество срабатываний, значит возможно есть место для упрощения.

  4. Может визуализацию ещё какую прикрутить …

Не то чтоб я от этого сильно счастлив, но всё остальное выглядит чрезмерным для моих скромных сил.

ugoday ★★★★★
() автор топика
Ответ на: комментарий от ugoday

Я как представляю со сколькими людьми придётся митинги митинговать, сердце в груди замирает.

Спасение утопающих - добровольное дело самих утопающих. Обложи тестами свои правила

Хотя бы поймаешь, когда твои кейсы накроются от чужого коммита

router ★★★★★
()

Логируй данные которые прогоняешь по условиям в проде. Держи дамп за Н дней. При изменений условий гоняй по ним дамп.

Реальные регекспы не математизируются, так что только подмножество реального сета данных.

ya-betmen ★★★★★
()