Я хочу найти и заменить каждое вхождение определенного шаблона десятичным числом, которое начинается с 1
и увеличивается на единицу для каждого совпадения.
Я могу найти аналогичные формулировки вопросов, которые, как оказалось, не об увеличении счетчика, а об изменении каждого совпадения на фиксированную величину. Другие подобные вопросы касаются вставки номеров строк, а не увеличения счетчика.
Пример, перед тем:
#1
#1.25
#1.5
#2
После:
#1
#2
#3
#4
В моих реальных данных гораздо больше текста, который я хочу переименовать.
substitute
hippietrail
источник
источник
perldo
, вы можете использовать:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Ответы:
Вам нужна подстановка с государством. Я помню , обеспечив (/?) Несколько полное решение для такого рода задач на SO.
Вот еще один способ продолжить (1). Теперь я перейду к 2 шагам:
Который дает:
Если вы не привыкли к VIM регулярных выражениям, я использую
:h /\zs
и\ze
указать , какие суб-шаблон я согласование, то я сортирую ряд цифр , возможно , с последующей точкой и другими цифрами. Это не идеально для любого числа с плавающей запятой, но этого здесь достаточно.Примечание: вам придется заключить его в пару функций + команда для простого интерфейса. Опять же, есть примеры SO / vim ( здесь , здесь , здесь ). В настоящее время я знаю достаточно vim, чтобы не заботиться о том, чтобы заключить этот трюк в команду. На самом деле я смогу написать эту команду с первой попытки, в то время как я запомню несколько минут, чтобы запомнить название команды.
(1) Цель состоит в том, чтобы поддерживать состояние промежуточных замен и заменить текущее вхождение чем-то, что зависит от текущего состояния.
Благодаря
:s\=
мы можем вставить что-то в результате вычислений.Остается проблема государства. Либо мы определяем функцию, управляющую внешним состоянием, либо обновляем сами себя. В C (и связанных с ним языках) мы могли бы использовать что-то вроде
length++
илиlength+=1
. К сожалению, в скриптах vim+=
нельзя использовать "из коробки". Это должно использоваться или с:set
или с:let
. Это означает, что:let length+=1
увеличивает число, но ничего не возвращает. Мы не можем писать:s/pattern/\=(length+=1)
. Нам нужно что-то еще.Нам нужны мутирующие функции. т.е. функции, которые изменяют свои входные данные. У нас есть
setreg()
,map()
,add()
и , вероятно , больше. Давайте начнем с них.setreg()
мутирует регистр. Отлично. Мы можем написатьsetreg('a',@a+1)
как в решении @Doktor OSwaldo. И все же этого недостаточно.setreg()
это больше процедура, чем функция (для тех из нас, кто знает Паскаль, Ада ...). Это значит, что ничего не возвращается. На самом деле, это что-то возвращает. Номинальный выход (то есть не исключительные выходы) всегда что-то возвращает. По умолчанию, когда мы забыли что-то вернуть, возвращается 0 - это также относится и к встроенным функциям. Вот почему в его решении выражение замены на самом деле\=@a+setreg(...)
. Хитрый, не правда ли?map()
также может быть использован. Если мы начнем со списка с одним 0 (:let single_length=[0]
), мы можем увеличить его благодаряmap(single_length, 'v:val + 1')
. Тогда нам нужно вернуть новую длину. В отличие отsetreg()
,map()
возвращает свой мутированный ввод. Это прекрасно, длина сохраняется в первой (и уникальной, и, следовательно, последней) позиции списка. Выражение замены может быть\=map(...)[0]
.add()
это тот, который я часто использую по привычке (хотяmap()
я и думал об этом , и я еще не оценил их выступления). Идеяadd()
заключается в том, чтобы использовать список в качестве текущего состояния и добавлять что-либо в конце перед каждой заменой. Я часто сохраняю новое значение в конце списка и использую его для замены. Какadd()
и возвращает его мутантный список ввода, мы можем использовать:\=add(state, Func(state[-1], submatch(0)))[-1]
. В случае с OP нам нужно только запомнить, сколько совпадений было обнаружено до сих пор. Достаточно вернуть длину этого списка состояний. Отсюда мой\=len(add(state, whatever))
.источник
\=
ожидает выражение, и потому что в отличие от C,i+=1
это не то, что делает приращение и возвращает выражение. Это означает, что\=
мне нужно что-то, что может изменить счетчик и вернуть выражение (равное этому счетчику). Пока что я нашел только функции манипулирования списком (и словарем). @Doktor OSwaldo использовал другую мутирующую функцию (setreg()
). разница в том, чтоsetreg()
никогда ничего не возвращает, а значит, всегда возвращает число0
.Но будьте осторожны, он перезапишет ваш регистр
a
. Я думаю, что это немного более прямо, чем ответ Люка, но, возможно, он быстрее. Если это решение чем-то хуже его, я хотел бы услышать любые отзывы, почему его ответ лучше. Любые отзывы для улучшения ответа будут высоко оценены!(Это также основано на моем SO-ответе /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )
источник
@a+setreg('a',@a+1)
корочеlen(add(t,1))
. В противном случае, это хороший другой подвох :). Я не думал об этом. Что касается использования функции замены словаря в замещающем тексте,:s
иsubstitute()
, как я заметил, это намного быстрее, чем явные циклы - отсюда и реализация моих функций списка в lh-vim-lib . Я думаю, что ваше решение будет на одном уровне с моим, может быть, немного быстрее, я не знаю.@a
неизменным. В скриптах это важно IMO. Будучи конечным пользователем в интерактивном режиме, я буду знать, какой регистр я могу использовать. Возиться с реестром менее важно. В моем решении в интерактивном режиме глобальная переменная смешана с; в сценарии это будет локальная переменная.+3
, я мог бы написать что-то вроде\=add(thelist, 3 + get(thelist, -1, 0))[-1]
.Я нашел похожий, но другой вопрос, который я задал пару лет назад, и сумел изменить один из его ответов, не полностью понимая, что я делаю, и он отлично работает:
В частности, я не понимаю, почему моя не использует
%
или почему я просто использую простую переменную, которую другие ответы почему-то избегают.источник
s//g
выражении. В любом случае это интересное решение. Возможно, @LucHermitte может рассказать вам больше о плюсах и минусах, поскольку мои знания о vimscript весьма ограничены по сравнению с его.printf()
хотя - как списки были введены в Vim 7. Но я должен признать , что я не ожидал бы (? / Не помню)<bar>
принадлежать к сфере:global
- IOW, сценарий я бы ожидать было применить:sub
на согласующих линий, то приращениеi
один раз в самом конце. Я ожидаю, что это решение будет немного медленнее. Но так ли это на самом деле важно? Важно то, как легко мы можем прийти с рабочим решением из памяти + проб и ошибок. Например, вимгольферы предпочитают макросы.g/s//
поведение в области видимости допускает другие грязные уловки. Так что спасибо вам обоим за интересные ответы и обсуждение, я не так часто учусь на них, давая ответы =).На этой странице уже есть три отличных ответа , но, как предложил Люк Эрмитт в комментарии , если вы делаете это не по назначению, важно то, как быстро и легко вы сможете найти работающее решение.
Таким образом, это проблема, которую я бы вообще не использовал
:substitute
: эту проблему легко решить, используя обычные команды нормального режима и рекурсивный макрос:(При необходимости) Во- первых, выключите
'wrapscan'
. Регулярное выражение мы будем использовать , будет соответствовать нужный текст результата, а также исходный текст, так и с'wrapscan'
на макрос в противном случае продолжить воспроизведение бы навсегда. (Или пока вы не поймете , что происходит , и нажмите<C-C>
.):Настройка условия поиска (используя то же базовое регулярное выражение уже упоминалась в существующих ответах):
(При необходимости) вернуться к первому совпадению, нажав
N
столько раз, сколько требуется,(При необходимости) Измените первое совпадение на нужный текст:
Очистите
"q
регистр и начните запись макроса:Вырвать текущий счетчик:
Перейти к следующему матчу:
Замените текущий счетчик на тот, который мы только что дернули:
Увеличить счетчик:
Играть в макрос
q
. Регистр"q
все еще пуст, потому что мы очистили его на шаге 5, поэтому на этом этапе ничего не происходит:Прекратить запись макроса:
Играй в новый макрос и смотри!
Как и во всех макросах, это выглядит как множество шагов при объяснении, как я делал выше, но учтите, что на самом деле их ввод очень быстро: кроме рекурсивного макроса-записи-шаблона они все просто обычные команды редактирования я выполняю все время во время редактирования. Единственный шаг, где я должен был сделать что-либо, даже приближающееся к мышлению, - это шаг 2, где я написал регулярное выражение для выполнения поиска.
Отформатированная в виде двух команд режима командной строки и серии нажатий клавиш, скорость решения этого типа становится более ясной: я могу придумать следующее почти так же быстро, как и набрать его 1 :
Я, вероятно, мог бы придумать другие решения на этой странице, немного подумав и сославшись на документацию 2 , но, как только вы поймете, как работают макросы, их действительно легко использовать с любой скоростью, которую вы обычно редактируете.
1: Там являются ситуации , когда макросы требуют больше мысли, но я считаю , что они не придумали много на практике. И, как правило, ситуации, в которых они возникают, - это единственное практическое решение , где используется макрос .
2: Не подразумевать, что другие ответчики не могли бы предложить свои решения так же легко: им просто нужны навыки / знания, которые лично у меня не так легко у меня под рукой. Но все пользователи Vim знают, как использовать обычные команды редактирования!
источник