Quero pesquisar e substituir cada ocorrência de um determinado padrão por um número decimal que comece em 1
e incrementa em um para cada correspondência.
Posso encontrar perguntas com palavras semelhantes que não se destinam a incrementar um contador, mas a modificar cada partida por um valor fixo. Outras perguntas semelhantes são sobre a inserção de números de linha, em vez de um contador de incremento.
Exemplo, antes:
#1
#1.25
#1.5
#2
Depois de:
#1
#2
#3
#4
Meus dados reais têm muito mais texto em torno das coisas que quero renumerar.
substitute
hippietrail
fonte
fonte
perldo
, você pode usar:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Respostas:
Você precisa de substituição por um estado. Lembro-me de ter fornecido uma (/ várias?) Solução completa para esse tipo de problemas no SO.
Aqui está outra maneira de proceder (1). Agora, vou prosseguir em 2 etapas:
Que dá:
Se você não está acostumado a vim regexes, eu uso
:h /\zs
e\ze
para especificar qual sub-padrão eu estou correspondendo, então eu correspondo a uma série de dígitos possivelmente seguidos por um ponto e outros dígitos. Isso não é perfeito para nenhum número de ponto flutuante, mas é o suficiente aqui.Nota: Você precisará agrupá-lo em uma função dupla + comando para uma interface simples. Novamente, existem exemplos no SO / vim ( aqui , aqui , aqui ) Atualmente, conheço o suficiente do vim para não me importar em envolver esse truque em um comando. Na verdade, poderei escrever este comando na primeira tentativa, enquanto levarei alguns minutos para lembrar o nome do comando.
(1) O objetivo é poder manter um estado entre substituições e substituir a ocorrência atual por algo que dependa do estado atual.
Graças a
:s\=
nós somos capazes de inserir algo resultante de uma computação.Continua sendo o problema do estado. Ou definimos uma função que gerencia um estado externo ou nos atualizamos como um estado externo. Em C (e idiomas relacionados), poderíamos ter usado algo como
length++
oulength+=1
. Infelizmente, nos scripts do vim,+=
não pode ser usado imediatamente. Ele precisa ser usado com:set
ou com:let
. Isso significa que:let length+=1
incrementa um número, mas não retorna nada. Nós não podemos escrever:s/pattern/\=(length+=1)
. Precisamos de outra coisa.Precisamos de funções mutantes. ou seja, funções que modificam suas entradas. Temos
setreg()
,map()
,add()
e provavelmente mais. Vamos começar com eles.setreg()
muda um registro. Perfeito. Podemos escreversetreg('a',@a+1)
como na solução do @Doktor OSwaldo. E, no entanto, isso não é suficiente.setreg()
é mais um procedimento do que uma função (para aqueles que conhecem Pascal, Ada ...). Isso significa que não retorna nada. Na verdade, ele retorna algo. Saída nominal (ou seja , saídas não-excepcionais ) sempre retorna algo. Por padrão, quando esquecemos de retornar algo, 0 é retornado - isso também se aplica às funções internas. É por isso que em sua solução a expressão de substituição é realmente\=@a+setreg(...)
. Complicado, não é?map()
também pode ser usado. Se começarmos de uma lista com um único 0 (:let single_length=[0]
), poderemos incrementá-la graças amap(single_length, 'v:val + 1')
. Então precisamos retornar o novo comprimento. Ao contráriosetreg()
,map()
retorna sua entrada mutada. Perfeito, o comprimento é armazenado na primeira (e única e, portanto, última) posição da lista. A expressão de substituição pode ser\=map(...)[0]
.add()
é o que eu costumo usar por hábito (eu pensei sobre issomap()
na verdade, e ainda não participei das respectivas apresentações). A idéiaadd()
é usar uma lista como o estado atual e acrescentar algo no final antes de cada substituição. Costumo armazenar o novo valor no final da lista e usá-lo para a substituição. Comoadd()
também retorna a sua lista de entrada mutante, podemos usar:\=add(state, Func(state[-1], submatch(0)))[-1]
. No caso do OP, precisamos apenas lembrar quantas correspondências foram detectadas até o momento. Retornar o comprimento dessa lista de estados é suficiente. Daí meu\=len(add(state, whatever))
.fonte
\=
espera uma expressão e, ao contrário de C,i+=1
não é algo que incrementa e retorna uma expressão. Isso significa que, por trás\=
, preciso de algo que possa modificar um contador e que retorne uma expressão (igual a esse contador). Até agora, as únicas coisas que encontrei são as funções de manipulação de lista (e dicionário). O @Doktor OSwaldo usou outra função de mutação (setreg()
). a diferença é quesetreg()
nunca retorna nada, o que significa que sempre retorna o número0
.Mas cuidado, ele substituirá seu registro
a
. Eu acho que é um pouco mais direto do que a resposta de luc, mas talvez a dele seja mais rápida. Se essa solução for de alguma forma pior que a dele, eu gostaria de ouvir qualquer comentário sobre por que sua resposta é melhor. Qualquer feedback para melhorar a resposta será muito apreciado!(Também é baseado em uma resposta SO da minha /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )
fonte
@a+setreg('a',@a+1)
é mais curto quelen(add(t,1))
. Caso contrário, este é um outro truque sujo :). Ainda não pensei nisso. Em relação ao uso de uma função de mutação de dicionário no texto de substituição, de:s
esubstitute()
, observei que isso é muito mais rápido que os loops explícitos - daí a implementação das funções de minha lista no lh-vim-lib . Eu acho que sua solução será igual à minha, pode ser um pouco mais rápida, eu não sei.@a
não é modificada. Nos scripts, isso é importante na IMO. Enquanto estiver no modo interativo, como usuário final, saberei qual registro posso usar. Mexer com um registro é menos importante. Na minha solução, no modo interativo, uma variável global é alterada; em um script, seria uma variável local.+3
, eu poderia escrever algo como\=add(thelist, 3 + get(thelist, -1, 0))[-1]
.Encontrei uma pergunta semelhante, mas diferente, que fiz há alguns anos e consegui alterar uma de suas respostas sem entender completamente o que estava fazendo e tem funcionado muito bem:
Especificamente, não entendo por que o meu não usa
%
ou por que apenas uso uma variável simples que as outras respostas evitam por algum motivo.fonte
s//g
instrução normal . Enfim, é uma solução interessante. Talvez o @LucHermitte possa lhe dizer mais sobre prós e contras, já que meu conhecimento sobre o vimscript é bastante limitado em comparação com o dele.printf()
entanto -, como as Listas foram introduzidas no Vim 7. Mas devo admitir que não esperava (/ não lembrava?) Que<bar>
pertencessem ao escopo de:global
- IOW, o cenário que eu esperava era aplicar as:sub
linhas correspondentes e incrementari
uma vez no final. Espero que esta solução seja um pouco mais lenta. Mas isso realmente importa? O importante é a facilidade com que conseguimos encontrar uma solução funcional da memória + tentativa e erro. Por exemplo, os Vimgolfers preferem macros.g/s//
comportamento do escopo permita outros truques sujos. Então, obrigado pelas respostas e discussões interessantes, muitas vezes não aprendo muito dando uma resposta =).Já existem três ótimas respostas nesta página, mas, como Luc Hermitte sugeriu em um comentário , se você está fazendo isso sem esforço, o importante é a rapidez e facilidade com que você pode encontrar uma solução em funcionamento.
Como tal, este é um problema que eu realmente não usaria
:substitute
: é um que pode ser facilmente resolvido usando comandos normais do modo normal e uma macro recursiva:(Se necessário) Primeiro, desligue
'wrapscan'
. A expressão regular que usaremos corresponderá ao texto do resultado desejado e ao texto inicial. Assim'wrapscan'
, a macro continuaria sendo reproduzida para sempre. (Ou até você perceber o que está acontecendo e pressionar<C-C>
.):Configure seu termo de pesquisa (usando a mesma expressão regular de base já mencionada nas respostas existentes):
(Se necessário) Volte para a primeira partida pressionando
N
quantas vezes for necessário,(Se necessário) Altere a primeira correspondência para o texto desejado:
Limpe o
"q
registro e comece a gravar uma macro:Arranque o contador atual:
Ir para a próxima partida:
Substitua o contador atual pelo que acabamos de puxar:
Incremente o contador:
Tocar macro
q
. O registro"q
ainda está vazio porque o limpamos na etapa 5; portanto, nada acontece neste momento:Pare de gravar a macro:
Jogue a nova macro e assista!
Como em todas as macros, isso parece muitas etapas quando explicado como fiz acima, mas observe que realmente digitá-las é muito rápido para mim: além do padrão-recursivo-macro-gravação-padrão, eles são todos regulares comandos de edição Estou executando o tempo todo durante a edição. O único passo em que eu tive que fazer qualquer coisa que se aproximasse do pensamento foi o passo 2, onde escrevi a expressão regular para realizar a pesquisa.
Formatada como dois comandos no modo de linha de comando e uma série de pressionamentos de tecla, a velocidade desse tipo de solução fica mais clara: posso conjurar o seguinte com a maior rapidez possível com a digitação 1 :
Provavelmente, eu poderia ter pensado nas outras soluções desta página com um pouco de reflexão e algumas referências à documentação 2 , mas, depois de entender como as macros funcionam, elas são fáceis de produzir na velocidade que você costuma editar.
1: Não são situações em que macros requerem mais pensamento, mas acho que eles não vêm-se muito na prática. E geralmente as situações em que ocorrem são aquelas em que uma macro é a única solução prática.
2: Para não implicar que os outros respondentes não poderiam ter encontrado suas soluções da mesma maneira facilmente: eles apenas exigem habilidades / conhecimentos que eu pessoalmente não tenho tão facilmente na ponta dos dedos. Mas todos os usuários do Vim sabem como usar comandos de edição regulares!
fonte