Захват квантификаторов и квантификатор арифметики

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

Захват квантификаторов

Кто-нибудь знает, позволяет ли аромат регулярных выражений захватывать квантификаторы? Под этим я подразумеваю, что будет подсчитано количество символов, совпадающих с квантификаторами, такими как + и *, и что это число можно будет снова использовать в другом квантификаторе.

Например, предположим, что вы хотели убедиться, что у вас есть одинаковое количество Ls и Rs в строке этого вида: LLLRRRRR

Вы можете представить себе такой синтаксис, как

L(+)R{\q1}

где квантификатор + для L захвачен, и где захваченное число упоминается в квантификаторе для R как {\q1}

Это было бы полезно, чтобы сбалансировать количество {@,=,-,/} в таких строках, как @@@@ "Star Wars" ==== "1977" ---- "Научная фантастика" //// " Джордж Лукас "

Отношение к рекурсии

В некоторых случаях захват квантификатора элегантно заменяет рекурсию, например фрагмент текста, обрамленный таким же количеством букв L и R,

L(+) some_content R{\q1} 

Идея представлена ​​в некоторых деталях на следующей странице: Захваченные квантификаторы

Здесь также обсуждается естественное расширение для захваченных квантификаторов: арифметика квантификаторов, для случаев, когда вы хотите сопоставить (3*x + 1) количество символов, сопоставленных ранее.

Я пытаюсь выяснить, существует ли что-то подобное.

Заранее спасибо за ваши идеи!!!

Обновить

Казимир дал фантастический ответ, который показывает два метода для проверки того, что различные части шаблона имеют одинаковую длину. Тем не менее, я не хотел бы полагаться на любой из них для повседневной работы. Это действительно трюки, которые демонстрируют отличное зрелищность. На мой взгляд, эти красивые, но сложные методы подтверждают предпосылку вопроса: функция регулярного выражения для захвата количества символов, которые могут быть сопоставлены квантификаторами (такими как + или *), сделала бы такие шаблоны балансировки очень простыми и расширила синтаксис в приятно выразительный способ.

Обновление 2 (намного позже)

Я обнаружил, что.NET имеет функцию, которая приближается к тому, о чем я спрашивал. Добавлен ответ, чтобы продемонстрировать функцию.

2 ответа

Решение

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

 @@@@ "Звездные войны" ==== "1977" ---- "Научная фантастика" //// "Джордж Лукас" 

Вы можете проверить, если @=-/ сбалансированы с этим шаблоном, который использует знаменитый трюк Qtax, (вы готовы?): "притяжательно-опциональная группа самоссылки "

~(?<!@)((?:@(?=[^=]*(\2?+=)[^-]*(\3?+-)[^/]*(\4?+/)))+)(?!@)(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))~

детали шаблона:

~                          # pattern delimiter
(?<!@)                     # negative lookbehind used as an @ boundary
(                          # first capturing group for the @
    (?:
        @                  # one @
        (?=                # checks that each @ is followed by the same number
                           # of = - /  
            [^=]*          # all that is not an =
            (\2?+=)        # The possessive optional self-referencing group:
                           # capture group 2: backreference to itself + one = 
            [^-]*(\3?+-)   # the same for -
            [^/]*(\4?+/)   # the same for /
        )                  # close the lookahead
    )+                     # close the non-capturing group and repeat
)                          # close the first capturing group
(?!@)                      # negative lookahead used as an @ boundary too.

# this checks the boundaries for all groups
(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))
~

Главная идея

Группа без захвата содержит только одну @, Каждый раз, когда эта группа повторяется, новый персонаж добавляется в группы захвата 2, 3 и 4.

притяжательно-опциональная группа, ссылающаяся на себя

Как это работает?

( (?: @ (?= [^=]* (\2?+ = ) .....) )+ )

При первом появлении символа @ группа захвата 2 еще не определена, поэтому вы не можете написать что-то подобное (\2 =) это сделает шаблон неудачным. Чтобы избежать проблемы, способ сделать обратную ссылку необязательной: \2?

Второй аспект этой группы заключается в том, что количество символов = matched увеличивается при каждом повторении группы без захвата, так как = добавляется каждый раз. Чтобы гарантировать, что это число всегда увеличивается (или шаблон не выполняется), притяжательный квантификатор заставляет обратную ссылку сначала сопоставляться перед добавлением нового = персонаж.

Обратите внимание, что эту группу можно увидеть так: если группа 2 существует, сопоставьте ее со следующей =

( (?(2)\2) = )

Рекурсивный путь

~(?<!@)(?=(@(?>[^@=]+|(?-1))*=)(?!=))(?=(@(?>[^@-]+|(?-1))*-)(?!-))(?=(@(?>[^@/]+|(?-1))*/)(?!/))~

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

детали шаблона:

(?<!@)                # left @ boundary
(?=                   # open a lookahead (to allow overlapped matches)
    (                 # open a capturing group
        @
        (?>           # open an atomic group
            [^@=]+    # all that is not an @ or an =, one or more times
          |           # OR
            (?-1)     # recursion: the last defined capturing group (the current here)
        )*            # repeat zero or more the atomic group
        =             #
    )                 # close the capture group
    (?!=)             # checks the = boundary
)                     # close the lookahead
(?=(@(?>[^@-]+|(?-1))*-)(?!-))  # the same for -
(?=(@(?>[^@/]+|(?-1))*/)(?!/))  # the same for /

Основное отличие от прецедентного паттерна состоит в том, что этот не заботится о порядке =- а также / групп. (Однако вы можете легко внести некоторые изменения в первый шаблон, чтобы справиться с этим, с классами символов и негативными взглядами.)

Примечание: для примера строки, чтобы быть более точным, вы можете заменить отрицательный вид сзади на якорь (^ или же \A). И если вы хотите получить всю строку как результат совпадения, вы должны добавить .* в конце (в противном случае результат матча будет пустым, как это заметит игривый).

Возвращаясь через пять недель, потому что я узнал, что в.NET есть кое-что, что очень близко подходит к идее "захвата кванторов", упомянутой в вопросе. Функция называется "балансировочные группы".

Вот решение, которое я придумал. Это выглядит долго, но это довольно просто.

(?:@(?<c1>)(?<c2>)(?<c3>))+[^@=]+(?<-c1>=)+[^=-]+(?<-c2>-)+[^-/]+(?<-c3>/)+[^/]+(?(c1)(?!))(?(c2)(?!))(?(c3)(?!))

Как это работает?

  1. Первая группа без захвата соответствует @ персонажи. В этой группе без захвата у нас есть три именованные группы c1, c2 и c3, которые ничего не соответствуют, или, скорее, соответствуют пустой строке. Эти группы будут служить тремя счетчиками c1, c2 и c3. Потому что.NET отслеживает промежуточные записи при количественной оценке группы, каждый раз @ при совпадении захват добавляется в коллекции захвата для групп c1, c2 и c3.

  2. Следующий, [^@=]+ съедает всех персонажей до первого =,

  3. Вторая количественная группа (?<-c1>=)+ соответствует = персонажи. Эта группа, кажется, названа -c1, но -c1 это не название группы. -c1 это синтаксис.NET для извлечения одного захвата из коллекции захвата группы c1 в эфир. Другими словами, это позволяет нам уменьшить c1. Если вы попытаетесь уменьшить значение c1, когда коллекция захвата пуста, совпадение не будет выполнено. Это гарантирует, что у нас никогда не будет больше = чем @ персонажи. (Позже мы должны убедиться, что мы не можем иметь больше @ чем = персонажи.)

  4. Следующие шаги повторяют шаги 2 и 3 для - а также / символы, уменьшающие счетчики c2 и c3.

  5. [^/]+ съедает остаток строки.

  6. (?(c1)(?!)) является условием, которое говорит: "Если группа c1 была установлена, то сбой". Вы можете знать, что (?!) это обычная уловка, чтобы заставить регулярное выражение потерпеть неудачу. Это условие гарантирует, что c1 был уменьшен до нуля: другими словами, не может быть больше @ чем = персонажи.

  7. Аналогично (?(c2)(?!)) а также (?(c3)(?!)) убедитесь, что не может быть больше @ чем - а также / персонажи.

Я не знаю о вас, но даже это немного долго, я нахожу это действительно интуитивным.

Другие вопросы по тегам