Vim help сообщает, что:

\1      Matches the same string that was matched by     */\1* *E65*
        the first sub-expression in \( and \). {not in Vi}
        Example: "\([a-z]\).\1" matches "ata", "ehe", "tot", etc. 

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

<paper-input label="Input label"> Some text </paper-input>
<paper-input label="Input label"> Some text </paper-inputa>
<aza> Some text </az>
<az> Some text </az>
<az> Some text </aza>

Я хотел сопоставить строки, в которых совпадают открывающий и закрывающий теги, например:

<paper-input label="Input label"> Some text </paper-input>
<az> Some text </az>

И мое тестовое регулярное выражение:

%s,<\([^ >]\+\).*<\/\1>,,gn

Но это соответствует строкам: 1, 3 и 4. То же самое с sed :

$ sed -ne 's,<\([^ >]\+\).*<\/\1>,\0,p' file
<paper-input label="Input label"> Some text </paper-input>
<aza> Some text </az>
<az> Some text </az>

Это: <\([^ >]\+\) должен быть жадным, и при попытке сопоставить его без \1 в конце все группы будут правильными. Но когда я добавляю \1, кажется, что <\([^ >]\+\) становится не жадным и пытается принудительно найти соответствие в 3-й строке . Может кто-нибудь объяснить, почему он соответствует строке 3rd:

<aza> Some text </az>

Это также демонстрация regex101

ПРИМЕЧАНИЕ Речь идет не о самом регулярном выражении (возможно, есть другой способ сделать это), а о поведении этого регулярного выражения.

3
Dave Grabowski 8 Сен 2016 в 03:43

3 ответа

Лучший ответ

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

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

%s,<\([^ >]\+\).*<\/\1>,,gn

Для строки три <aza> Some text </az>,

Механизм регулярных выражений смотрит на \1 = aza. и видит, соответствует ли .*</aza> остальной части строки. Это не так, поэтому он выбирает что-то другое для \1. В следующий раз он выбирает \1 = az и проверяет, совпадает ли .*</az> с остальной частью строки, и делает это. Итак, строка соответствует

(Это упрощенная версия. Я пропустил тот факт, что .* потенциально может сам выполнять большое количество откатов)


Решить это так же просто, как добавить якорь в регулярное выражение, не позволяя регулярному выражению искать другие значения, которые могли бы удовлетворить \1. В этом случае достаточно пробела или >.

3
FDinoff 8 Сен 2016 в 20:26

Вам нужно добавить \>, чтобы указать конец слова . Могут быть и другие решения с узорами нулевой ширины, но это усложнит ситуацию.

Кроме того, ваш разделитель ,, а не /

Который дает:

%s,<\([^ >]\+\)\>.*</\1>,,gn
2
Luc Hermitte 8 Сен 2016 в 01:42

В настоящее время причина, по которой строка 3 (<aza>) отображается как совпадение, заключается в том, что термин .* в вашем регулярном выражении может совпадать в нескольких строках. Итак, строка 3 соответствует, потому что строка 5 имеет закрывающий тег. Чтобы исправить это, заставьте регулярное выражение найти соответствующий закрывающий тег только в той же строке:

%s,<\([^ >]\+\)[^\n]*?<\/\1>,,gn
               ^^^^^ use [^\n]* instead of .*
0
Tim Biegeleisen 8 Сен 2016 в 00:54