Как найти / заменить n-е вхождение в визуальном режиме vim? - PullRequest
2 голосов
/ 27 апреля 2020

Это работает:

'<,'>s/\v\/\zs(\/)// 
'<,'>s/\v(\/)@<=\//BAR/

Мне просто интересно, есть ли более простой способ заменить n-ное вхождение на {} или что-то в vim.

Заменить третьего форварда sla sh '/'.

/dir1//fas//fooBar/¬
/dir2//\.foobar//fas/¬
/dir//.foo//fas/¬

Как бы заменить четвертый' foo '?

foo foo foo foo foo foo foo
foo foo foo foo foo foo foo

1 Ответ

1 голос
/ 27 апреля 2020

Я расскажу о том, как сопоставить эти шаблоны для простоты, замена их в команде замены должна работать так же, используя тот же шаблон в команде :s.

Заменить третий вперед sla sh '/'.

С совпадением из одного символа это проще, поскольку вы можете использовать [^/] для поиска символов, не являющихся частью совпадения.

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

. В этот момент вы можете сопоставить два экземпляра «not косые черты», за которыми следует «sla *». 1111 * ", а затем на третьем вы можете использовать \zs, чтобы отметить его как начало фактического совпадения.

Немного прискорбно, что самому / нужно будет экранировать с помощью \/ если мы используем его для сравнения, но получающийся в результате шаблон:

/\v^%([^\/]*\/){2}[^\/]*\zs\/

Один общий совет для шаблонов, которые включают /, - это поиск в обратном направлении с использованием ?, поэтому давайте сделаем это для улучшить читаемость:

?\v^%([^/]*/){2}[^/]*\zs/

Шаблоны Элементы шаблона, которые я здесь использовал, могут быть незнакомы для некоторых из них:

  • %(...): группирует шаблон так же, как (...), но не создает группу захвата.
  • {2}: точно соответствует предыдущему шаблону.

Помните, что мы используем "verymagi c" с \v , поэтому большинству из вышеперечисленного не потребуются обратные слэши.

Есть аккуратный ярлык, который мы можем использовать, чтобы сократить шаблон выше (и это поможет нам, когда мы посмотрим на случай более длинного слова) , то есть если у вас есть \zs в нескольких местах в вашем паттерне, то последним будет соответствовать тот, который будет определять фактическое начало матча. (См. :help /\zs.)

Таким образом, мы можем упростить это до:

?\v^%([^/]*\zs/){3}

Мы сопоставляем слова "не косые черты", за которыми следует "sla sh" три раза. \zs вступит в силу только в последнем (третьем) матче, поэтому вы в конечном итоге будете сопоставлять третий sla sh в строке.

Теперь давайте перейдем к более сложному случаю сопоставления. слово:

Как бы я заменил четвертый 'foo'?

Здесь мы не можем использовать [^...], чтобы соответствовать "не foo". Я имею в виду, мы могли бы использовать что-то вроде \v([^f]|f[^o]|fo[^o]), но оно быстро растет по мере того, как растет слово, которое вы подходите. И есть лучший способ сделать это.

Мы можем использовать отрицательный взгляд нулевой ширины! См. :help /\@<! для этого интересного оператора. Короче говоря, он берет предыдущий атом (мы будем использовать группу со словом здесь) и проверяет, соответствует ли этот элемент , а не соответствию, заканчивающемуся в этом месте.

Таким образом, мы можем использовать this:

/\v^%(%(.%(foo)@<!)*\zsfoo){4}

%(foo)@<! здесь гарантирует, что каждый ., который мы сопоставим, не будет последним o в foo. Таким образом, мы можем точно посчитать первый, второй, третий и четвертый foo в строке и убедиться, что мы не будем совпадать с пятым, шестым или седьмым.

Здесь снова мы используем хитрость повторяя его четыре раза (чтобы найти четвертое совпадение) и имея последнюю \zs палку.

Обратите внимание, что отрицательный просмотр хорошо работает с фиксированным словом, но если вы начинаете иметь мультисис, такие как * или + et c. тогда все становится намного сложнее. Посмотрите на справку для оператора и предупреждения о том, что она может быть медленной. Есть также вариант оператора, который ограничивает количество символов, которые он будет искать, что вам не нужно строго при сопоставлении с фиксированным словом, но может быть полезно при более общем сопоставлении.

Один интересный тест случай для этого - совпадение с повторениями, например fofo, и текст с повторениями таких, как fofofo или fofofofo.

Фактически, тестирование на тех, кто сделал меня обратите внимание, что шаблон выше на самом деле предпочтет сопоставить второе вхождение в fofofo, а не первое, если это четвертое вхождение fofo в этой строке. Это потому, что оператор * является жадным. Мы можем исправить это, используя вместо этого {-}, что соответствует кратчайшей возможной последовательности.

Исправляя эту ошибку, мы получаем:

/\v^%(%(.%(foo)@<!){-}\zsfoo){4}

Что достаточно универсально, и вы, вероятно, можете использовать любое фиксированное слово или даже шаблон с несколькими вариациями (например, падеж, множественное число, альтернативное написание, et c.)

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