Обратное отношение регулярных выражений Vim / sed в шаблоне поиска
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>,,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 demo
Примечание Речь идет не о самом регулярном выражении (вероятно, есть другой способ сделать это), а о поведении этого регулярного выражения.
3 ответа:
Чтобы понять, почему ваше регулярное выражение ведет себя так, как оно ведет себя, вам нужно понять, что делает механизм обратного следования регулярных выражений.
Движок будет жадно подбирать и потреблять столько символов, сколько сможет. Но если он не находит соответствия, он возвращается назад и пытается найти другое соответствие, которое все еще удовлетворяет шаблону.%s,<\([^ >]\+\).*<\/\1>,,gn
Для третьей строки
Движок регулярных выражений смотрит на<aza> Some text </az>
,\1 = aza
. и видит, совпадает ли.*</aza>
с остальной строкой. Это не так, поэтому он выбирает что-то остальное за\1
. В следующий раз он выбирает\1 = az
и видит, совпадает ли.*</az>
с остальной строкой, и он это делает. Таким образом, строка соответствует(это упрощенная версия. Я пропустил тот факт, что
.*
потенциально может сделать много отступлений сам)
Решение этой задачи так же просто, как добавление якоря в регулярное выражение, останавливающее регулярное выражение от поиска других значений, которые могли бы удовлетворить\1
. В этом случае достаточно совпадения пространства или>
.
Вам нужно добавить
\>
, чтобы указать конец слова. Могут быть и другие решения с узорами 0-ширины, но это все усложнит.Кроме того, ваш разделитель
,
, а не/
Что дает:
%s,<\([^ >]\+\)\>.*</\1>,,gn
В настоящее время причина, по которой строка 3 (
<aza>
) отображается как совпадение, заключается в том, что термин.*
в вашем регулярном выражении может совпадать через несколько строк. Таким образом, строка 3 совпадает, потому что строка 5 имеет закрывающий тег. Чтобы исправить это, заставьте регулярное выражение найти соответствующий закрывающий тег на той же самой строке только:%s,<\([^ >]\+\)[^\n]*?<\/\1>,,gn ^^^^^ use [^\n]* instead of .*