Regex: захват групп в группах захвата

Введение

(вы можете пропустить до Что делать, если..., если вам скучно с помощью интро)

Этот вопрос не относится к VBScript в частности (я просто использовал его в этом случае): я хочу найти решение для использования общих регулярных выражений (включая редакторы).

Это началось, когда я хотел создать адаптацию Пример 4, где 3 группы захвата используются для разделения данных по 3 ячейкам в MS Excel. Мне нужно было захватить одну общую картину, а затем, внутри нее, захватить еще 3 шаблона. Тем не менее, в том же выражении мне также нужно было захватить еще один вид рисунка и снова захватить еще 3 паттерна внутри него (да, я знаю... но прежде чем указывать пальцем нудиус, пожалуйста, закончите чтение).

Я думал сначала о Named Capturing Groups, тогда я понял, что не должен "смешивать названные и пронумерованные группы захвата", так как это "не рекомендуется, потому что ароматы несовместимы в том, как нумеруются группы".

Затем я просмотрел VBAcript SubMatches и "не захватывающий" Groups, и я получил рабочее решение для конкретного случая:

For Each C In Myrange
    strPattern = "(?:^([0-9]+);([0-9]+);([0-9]+)$|^.*:([0-9]+)\s.*:([0-9]+).*:([a-zA-Z0-9]+)$)"

    If strPattern <> "" Then
        strInput = C.Value

        With regEx
            .Global = True
            .MultiLine = True
            .IgnoreCase = False
            .Pattern = strPattern
        End With

        Set rgxMatches = regEx.Execute(strInput)

        For Each mtx In rgxMatches
            If mtx.SubMatches(0) <> "" Then
                C.Offset(0, 1) = mtx.SubMatches(0)
                C.Offset(0, 2) = mtx.SubMatches(1)
                C.Offset(0, 3) = mtx.SubMatches(2)
            ElseIf mtx.SubMatches(3) <> "" Then
                C.Offset(0, 1) = mtx.SubMatches(3)
                C.Offset(0, 2) = mtx.SubMatches(4)
                C.Offset(0, 3) = mtx.SubMatches(5)
            Else
                C.Offset(0, 1) = "(Not matched)"
            End If
        Next
    End If
Next

Вот демо в Rubular регулярного выражения. В них:

124; 12; 3
мой id1: 213 мой id2: 232 мое слово: ins4yanrgx
: 8587459: 18254182540215: dcpt
0; 1; 2

Он возвращает первые 2 ячейки с числами и 3 rd с числом или словом. В основном я использовал группу без захвата с двумя "родительскими" паттернами ( "родители" = широкие шаблоны, где я хочу обнаружить другие подматрицы). Если родительский шаблон 1 st имеет соответствующий поднабор (1 st), тогда я помещаю его значение и оставшиеся захваченные группы этого шаблона в 3 ячейки. Если нет, я проверяю, была ли сопоставлена ​​группа захвата 4 th (принадлежащая родительскому шаблону 2 nd) и поместить остальные подматрицы в те же 3 ячейки.

Что делать, если...

Вместо того, чтобы иметь что-то вроде этого:

(?:^(\d+);(\d+);(\d+)$|^.*:(\d+)\s.*:(\d+).*:(\w+)$|what(ever))

Возможно, что-то подобное:

(#:^(\d+);(\d+);(\d+)$)|(#:^.*:(\d+)\s.*:(\d+).*:(\w+)$)|(#:what(ever))

Где (#: вместо создания группы, не содержащей захвата, создаст "родительскую" нумерованную группу захвата. Таким образом, я мог бы сделать что-то похожее на Пример 4:

C.Offset(0, 1) = regEx.Replace(strInput, "#$1")
C.Offset(0, 2) = regEx.Replace(strInput, "#$2")
C.Offset(0, 3) = regEx.Replace(strInput, "#$3")

Он будет искать родительские шаблоны, пока не найдет совпадение в дочернем шаблоне (первое совпадение будет возвращено и, в идеале, не будет искать остальные).

Есть ли что-то вроде этого? Или я что-то скрываю от регулярного выражения, которое позволяет это сделать?

Другие возможные варианты:

  • обратитесь к родительскому и дочернему шаблонам напрямую, например: #2$3 (это было бы эквивалентно $6 в моем примере);
  • создайте столько групп захвата, сколько необходимо в других (я думаю, это было бы более сложным, но также и самой интересной частью), например: с регулярным выражением (такой же синтаксис), как (#:^_(?:(#:(\d+):\w+-(\d))|(#:\w+:(\d+)-(\d+)))_$)|(#:^\w+:\s+(#:(\w+);\d-(\d+))$) и выборка ##$1 в шаблонах, таких как:

    _123:smt-4_ он будет соответствовать: 123
    _ott:432-10_ он будет соответствовать: 432
    yant: special;3-45235 он будет соответствовать: специальному

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

Ответ 1

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

Существует конструкция регулярного выражения для того, что называется Branch Reset.
Его предлагается на большинстве совместимых с Perl двигателей. Не Java и Dot Net.
Он в основном просто сохраняет ресурсы регулярных выражений и упрощает обработку совпадений.

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

(ниже была построена с помощью RegexFormat 6)

Вот ветка reset версия:

 # (?|^(\d+);(\d+);(\d+)$|^.*:(\d+)\s.*:(\d+).*:(\w+)$|what(ever)()())

 (?|
      ^ 
      ( \d+ )                       # (1)
      ;
      ( \d+ )                       # (2)
      ;
      ( \d+ )                       # (3)
      $ 
   |  
      ^ .* :
      ( \d+ )                       # (1)
      \s .* :
      ( \d+ )                       # (2)
      .* :
      ( \w+ )                       # (3)
      $ 
   |  
      what
      ( ever )                      # (1)
      ( )                           # (2)
      ( )                           # (3)
 )

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

 # (?:^(\d+);(\d+);(\d+)$|^.*:(\d+)\s.*:(\d+).*:(\w+)$|what(ever))

 (?:
      ^ 
      ( \d+ )                       # (1)
      ;
      ( \d+ )                       # (2)
      ;
      ( \d+ )                       # (3)
      $ 
   |  
      ^ .* :
      ( \d+ )                       # (4)
      \s .* :
      ( \d+ )                       # (5)
      .* :
      ( \w+ )                       # (6)
      $ 
   |  
      what
      ( ever )                      # (7)
 )

и

    # (#:^(\d+);(\d+);(\d+)$)|(#:^.*:(\d+)\s.*:(\d+).*:(\w+)$)|(#:what(ever))

    (                             # (1 start)
         \#: ^ 
         ( \d+ )                       # (2)
         ;
         ( \d+ )                       # (3)
         ;
         ( \d+ )                       # (4)
         $ 
    )                             # (1 end)
 |  
    (                             # (5 start)
         \#: ^ .* :
         ( \d+ )                       # (6)
         \s .* :
         ( \d+ )                       # (7)
         .* :
         ( \w+ )                       # (8)
         $ 
    )                             # (5 end)
 |  
    (                             # (9 start)
         \#:what
         ( ever )                      # (10)
    )                             # (9 end)