Яка різниця між атомами '\ zs' та '\ @ <=' у регулярному виразі Vim?


12

Це те, що я отримую з документації: \zs"запускає виділену частину" після відповідності попередньому регексу та \@<="запускає виділену частину" після відповідності попередньому атому . Але я точно не розумію тонкощів цього, тому хтось може пояснити, чим вони відрізняються трохи більше за глибиною?

Це мене викликало цікавість: якщо я біжу

/\_s\zsnnoremap

тобто виберіть nnoremapпередує пробіл або початковий рядок (тобто новий рядок із попереднього рядка, отже, і \_попередній s), а потім запустіть, gnщоб увійти у візуальний режим і візуально вибрати наступний збіг, чомусь лише перший стовпець (тобто перший nв nnoremap) обраний - незважаючи на те , що все nnoremapслово виділене з :hlsearchвключено.

Однак якщо я замість цього запускаю пошук

/\_s\@<=nnoremap

а потім спробуйте gn, весь nnoremapправильно обраний. Що тут може статися? Чи я (зважусь сказати) виявив якусь незрозумілу помилку?


Я думаю, :h patternsщо це є, але моя пам'ять говорить про те, що регулярні вирази складаються з атомів, якщо це допоможе пояснити різницю.
Д. Бен Нобл

Відповіді:


15

Схоже, ви дійсно знайшли незрозумілу помилку. Я впровадив gntextobject ще в 2012 році для Vim 7.3 щось. В основному це працює наступним чином:

1) Він здійснює пошук за останнім збігом поточного регулярного виразу.

2) Він шукає вперед для наступної відповідності поточного регулярного виразу.

Це повинно дати зрозуміти, що курсор буде на початку наступного матчу, навіть якщо він був там уже на початку 1). Нарешті

3) він шукає кінець поточного регулярного виразу. і кладе курсор туди.

Зараз тут відбувається те, що пошук кінця поточного матчу завершується і повертається до кінця попереднього матчу (тому що wrapscanвін уже встановлений після відключення для 1)). Потім він встановлює візуальний маркер на область від початку (кінець точки 2) і область, на яку переміщується наступний елемент пошуку 3).

Я детальніше ознайомлюся з проблемою і, ймовірно, пізніше надішлю виправлення для Vim.

[Оновлення 22.05.2018] Я написав і надіслав патч, щоб виправити цю поведінку.

[Оновлення2 22.05.2018] І патч об’єднано як рівень патчу 8.1.0018

[Оновлення 22.10.2019] Станом на патч Vim 8.1.629 третій крок більше не виконується. Натомість Vim тепер може визначити кінець матчу, коли знаходить початок матчу (крок 2)


8

Крістіан повністю вирішив питання про поведінку баггі gn, але все ще існують принципові відмінності між \zsта \@<=. Найбільша істота \@<=модифікує попередній атом, а \zsатом в iself.

Поміркуйте:

Xnnoremap

\%1cX\zsnnoremap     (regex 1)
\%1cX\@<=nnoremap    (regex 2)
\%2cX\@<=nnoremap    (regex 3)

Regex 1 відповідає, оскільки \%1cвідповідає стовпцю 1 і там є X. \zsпросто призводить до того, що матч буде перезапущений у позицію після X.

Regex 2, однак, не відповідає, оскільки, хоча \%1cвідповідає першому стовпцю, X\@<=дорівнює нульовій ширині (як зазначено в документації) і nnoremapпочинається з стовпця 2. Немає нічого для складання різниці позицій між стовпцями 1 і 2.

Regex 3 відповідає з nnoremapпочатку в колонці 2.


1
Я не думаю, що регулярний вираз 2 не виходить з ладу, тому що немає нічого, щоб скласти різницю позицій між стовпцями 1 і 2. Якщо це було проблемою, вилучення nnoremapз регулярного вираження призведе до відповідності; але регулярний вираз все ще не вдається. Я думаю, що це не вдається, оскільки \%1cX\@<=висловлює позицію, яка не може існувати. \%1cспівпадає з позицією в стовпці 1 і X\@<=просить, щоб персонаж Xзбігався до цього. Але перед першим стовпцем не може бути жодного символу. Ось чому, навіть якщо ви заміните Xкрапку (будь-який символ), регулярний вираз \%1c.\@<=все одно виходить з ладу.
user938271

4

\zsзастосовується до всього регулярного виразу і встановлює наступний символ як перший символ усього збігу. Все, що перед \zsзаповітом, не буде включено до складу відповідного тексту.

\@<=з іншого боку, впливає лише на атоми безпосередньо навколо нього, дозволяючи вказати, що наступний атом буде відповідати лише у тому випадку, якщо він слідує за попереднім атомом. Так, наприклад, регулярний вираз:

\vbar.*(foo)@<=bar

Буде збігатися з усім текстом між двома екземплярами bar(включаючи самі екземпляри), але тільки якщо другому передує foo. тобто воно буде відповідати:

barbazfoobar

але ні:

barbazbazbar

Оскільки \@<=локалізовано таким чином, ви навіть можете використовувати \@<=кілька разів в одному виразі:

\vbar.*(foo)@<=bar.*(foo)@<=bar

Далі буде відповідати три екземпляри bar, але лише якщо другим двом передує foo.

тобто з урахуванням тексту:

barfoobarbazfoobar
barfoobarbazbazbar
barbazbarbazfoobar

Він відповідатиме лише першому рядку.


Але ви можете обміняти перший з переглядом тому \zs, тобто, це також повинно працювати: \vfoo\zsbar.*(foo)@<=bar.
Карл Інгве Лервег

@ KarlYngveLervåg Добрий момент. Я редагував, щоб зробити чіткіше розрізнення та використовувати приклади, де \zsїх взагалі не можна замінити.
Багатий

Отже, на моє розуміння, \zsі \zeїх можна замінити оглядом регекс-шаблонів, і вони більш потужні, правда? Більш потужна причина, що їх можна використовувати не один раз і їх можна згрупувати \(\). А також тому, що вони працюють як огляд Perge навколо регулярних виразів. Щось помилилося?
klaus

1
@klaus Це дуже добре для мене (хоча я не експерт). Зауважте, що ви повинні використовувати \zs/ \zeколи можете, оскільки вони швидші, ніж зовні.
Багатий

Зрозумів. А \zsта \ze, очевидно , більш інтуїтивним. Дякую за пояснення.
klaus
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.