4

Пытаюсь сделать минипарсер формулы, на выходе должно быть 5 групп:

(1)(2)(3)(4)(5)

Есть 3 возможных варианта:

  1. Если после буква заглавная, то после неё может следовать 'w', вторая группа в этом случае пустая:

    ([2-3]?)()([ACD]{1})([w]?)([2']?)

  2. Во втором случае вторая и четвёртая группы всегда пустые:

    ([2-3]?)()([acd]{1})()([2']?)

  3. Если вначале 2 цифры, то обязательно через '-' и обязательно первая меньше второй... т.е. тут нужно как-то исключить вариант '3-3' из возможных. Четвёртая группа в этом случае всегда пустая.

    ([2-3]{1})-([3-5]{1})([acd]{1})()([2']?)

Как объединить эти три правила в одну формулу? Может быть, что-то можно записать компактнее?

Isaev
  • 2,455
  • В п. 3 числа произвольные? – wcobalt Jan 22 '19 at 10:38
  • @wcobalt первое число 2 или 3, второе любое от 3 до 5 – Isaev Jan 22 '19 at 10:47
  • но, если первое 3, то второе от 4 до 5 – Isaev Jan 22 '19 at 10:55
  • 1
    Меня могут закидать помидорами, но для объединения выражений используют |. Необходимо учитывать порядок вариантов, чтобы предыдущий вариант не перекрывал текущий, который больше подходит для ситуации. – Adokenai Jan 22 '19 at 14:33
  • @Adokenai да, но сбивается номерация групп в этом случае... или есть какая-то хитрость неизвестная мне... Получается вот такая колбаса: https://regex101.com/r/gRnbTK/1 Всё бы ничего, но при первом вхождении у нас группы 1-5, как и хотелось бы, при совпадении со вторым вариантом группы 6-10, с третьим 11-17 (там вообще аномалия какая-то, группа без захвата, но нумерация идёт) – Isaev Jan 22 '19 at 16:09
  • @Isaev Третья группа вообще нужна? Да и само построение выражения вызывает вопросы. Не понятны пустые скобки. вот как я переделал – Adokenai Jan 22 '19 at 22:51
  • @Adokenai третья группа нужна, она понимает выражения начинающиеся с двух чисел через тире, твой вариант их теперь не видит, и апострофы вконце тоже не видит... пустые скобки добавлены, чтобы найденные логические единицы попадали всегда в свою группу, т.к. иначе в каждом варианте количество групп разное, чтобы потом не встраивать логику в программу по выбору информации с разных мест на основе количества групп на выходе – Isaev Jan 23 '19 at 09:16
  • @Isaev Если не секрет, что за супер формулы такие? – Adokenai Jan 23 '19 at 14:20
  • @Adokenai из языка вращений шарнирных головоломок – Isaev Jan 23 '19 at 14:21
  • В общем получилось следующее: https://regex101.com/r/gRnbTK/2 Работает корректно, если можно оптимизировать, буду раз предложениям – Isaev Jan 23 '19 at 15:20
  • @Ver Nick приз в студию) – Isaev Jan 25 '19 at 08:31
  • 1
    @Isaev А на каком языке вы используете эти регулярки ? В PCRE совместимых выражениях есть именованные группы. И если у языка использующего эту библиотеку есть интерфейс к ним, то можно в нескольких ветвях выбора использовать одно и то же имя. на выходе получите под именами захваты из сработавшего варианта. – Mike Jan 27 '19 at 21:29
  • @Mike но одинакого именовать группы вроде недопустимо, или? В данном случае Delphi, там вроде питоновский синтаксис регулярок, т.ч. должно быть, но я никогда не пробовал там с именованными группами работать, надо заценить – Isaev Jan 29 '19 at 11:13
  • @Isaev В перле я пользовался одинаковыми именованными группами. Там проблем не было. но perl все таки законодатель моды в регулярках. Его новшества долго расходятся по другим программам – Mike Jan 29 '19 at 13:46
  • Последняя версия получилась такая: https://regex101.com/r/gRnbTK/3 Это работает правильно, как и было задумано, но очень медленно, можно видеть, что для этого примера потребовалось аж 2588 шагов... Хотелось бы это оптимизировать, т.к. критическое место. Знаю тут есть гуру в этой теме, помогали уже несколько раз в вопросах оптимизации регулярок. Могу оформить новым вопросом или в рамках конкурса оставить тут? – Isaev Feb 04 '19 at 10:17
  • 1
    Сложно такое оптимизировать. Пока что предложу https://regex101.com/r/RMuVMt/2 – Wiktor Stribiżew Feb 14 '19 at 12:55

1 Answers1

1

Немного оптимизированное для PCRE выражение на основе групп со сбросом нумерации захватывающих подмасок будет выглядеть как

(?<![^ ,\n])
(?|
  ([23]?)()([MSEURFBDL])(w?)|
  ([2-5]?)()([mseurfbdl])()|
  (?|(2)-([3-5])|(3)-([45]))([mseurfbdl])()
)
([2']?)
(?![^, \n])

См. пример работы выражения онлайн.

Подробности

  • (?<![^ ,\n]) - сразу перед текущей позицией не должно быть символа, отличного от пробела, запятой или сивола перевода строки
  • (?| - группа со сбросом нумерации подмасок:
    • ([23]?)()([MSEURFBDL])(w?)| - опциональная цифра 2 или 3 (№1), пустая строка (№2), буква M, S, E, U, R, F, B, D или L (№3) и опциональная буква w (№4)
    • ([2-5]?)()([mseurfbdl])()| - опциональная цифра от 2 до 5 (№1), пустая строка (№2), буква m, s, e, u, r, f, b, d или l (№3) и пустая строка (№4)
    • (?|(2)-([3-5])|(3)-([45]))([mseurfbdl])() - цифра 2 (№1), дефис, цифра от 3 до 5 (№2) ИЛИ 3 (№1), дефис, цифра 4 или 5 (№2), буква m, s, e, u, r, f, b, d или l (№3) и пустая строка (№4)
  • ) - конец первой группы
  • ([2']?) - Группа №5: опциональный символ 2 или '
  • (?![^, \n]) - сразу после текущей позиции не должно быть символа, отличного от пробела, запятой или сивола перевода строки.