Quiero buscar y reemplazar cada aparición de un cierto patrón con un número decimal que comienza en 1
y se incrementa en uno para cada coincidencia.
Puedo encontrar preguntas redactadas de manera similar que no se trata de incrementar un contador sino de modificar cada partida en una cantidad fija. Otras preguntas similares son sobre insertar números de línea en lugar de un contador incremental.
Ejemplo, antes:
#1
#1.25
#1.5
#2
Después:
#1
#2
#3
#4
Mis datos reales tienen mucho más texto alrededor de las cosas que quiero volver a numerar.
substitute
hippietrail
fuente
fuente
perldo
, puede usar:%perldo s/#\K\d+(\.\d+)?/++$i/ge
Respuestas:
Necesitas sustitución con un estado. Recuerdo haber proporcionado una solución completa (¿/ varias?) Para este tipo de problemas en SO.
Aquí hay otra forma de proceder (1). Ahora, procederé en 2 pasos:
Lo que da:
Si no está acostumbrado a vim regexes, utilizo
:h /\zs
y\ze
para especificar qué subpatrón estoy haciendo coincidir, entonces hago coincidir una serie de dígitos posiblemente seguidos por un punto y otros dígitos. Esto no es perfecto para ningún número de coma flotante, pero aquí es suficiente.Nota: Tendrás que envolverlo en un par de funciones + comando para una interfaz simple. Nuevamente, hay ejemplos en SO / vim ( aquí , aquí , aquí ) Hoy en día sé lo suficiente de vim como para no importarme incluir este truco en un comando. De hecho, podré escribir este comando en el primer intento, mientras que me tomaré varios minutos para recordar el nombre del comando.
(1) El objetivo es poder mantener un estado entre sustituciones y reemplazar la ocurrencia actual con algo que depende del estado actual.
Gracias a que
:s\=
podemos insertar algo resultante de un cálculo.Sigue siendo el problema del estado. O definimos una función que gestiona un estado externo, o nos actualizamos a nosotros mismos un estado externo. En C (y lenguajes relacionados), podríamos haber usado algo como
length++
olength+=1
. Desafortunadamente, en las secuencias de comandos vim,+=
no se puede usar de fábrica. Debe usarse con:set
o con:let
. Esto significa que:let length+=1
incrementa un número, pero no devuelve nada. No podemos escribir:s/pattern/\=(length+=1)
. Necesitamos algo mas.Necesitamos funciones mutantes. es decir, funciones que mutan sus entradas. Tenemos
setreg()
,map()
,add()
y probablemente más. Comencemos con ellos.setreg()
Muta un registro. Perfecto. Podemos escribirsetreg('a',@a+1)
como en la solución de @Doktor OSwaldo. Y sin embargo, esto no es suficiente.setreg()
es más un procedimiento que una función (para quienes conocemos a Pascal, Ada ...). Esto significa que no devuelve nada. En realidad, devuelve algo. La salida nominal (es decir , salidas no excepcionales ) siempre devuelve algo. Por defecto, cuando olvidamos devolver algo, se devuelve 0 ; también se aplica con las funciones integradas. Es por eso que en su solución la expresión de reemplazo es en realidad\=@a+setreg(...)
. Tricky, ¿no es así?map()
También podría ser utilizado. Si comenzamos desde una lista con un solo 0 (:let single_length=[0]
), podríamos incrementarlo gracias amap(single_length, 'v:val + 1')
. Entonces necesitamos devolver la nueva longitud. A diferenciasetreg()
,map()
devuelve su entrada mutada. Eso es perfecto, la longitud se almacena en la primera posición (y única, y por lo tanto también la última) de la lista. La expresión de reemplazo podría ser\=map(...)[0]
.add()
es el que uso habitualmente por costumbre (ya lo he pensado enmap()
realidad, y todavía no he hecho sus respectivas presentaciones). La ideaadd()
es utilizar una lista como el estado actual y agregar algo al final antes de cada sustitución. A menudo almaceno el nuevo valor al final de la lista y lo uso para el reemplazo. Comoadd()
también devuelve su lista de entrada mutado, podemos utilizar:\=add(state, Func(state[-1], submatch(0)))[-1]
. En el caso de OP, solo necesitamos recordar cuántas coincidencias se han detectado hasta ahora. Devolver la longitud de esta lista de estados es suficiente. De ahí mi\=len(add(state, whatever))
.fuente
\=
espera una expresión y porque, a diferencia de C,i+=1
no es algo que incremente y devuelva una expresión. Esto significa que detrás\=
necesito algo que pueda modificar un contador y que devuelva una expresión (igual a ese contador). Hasta ahora, lo único que he encontrado son las funciones de manipulación de listas (y diccionarios). @Doktor OSwaldo ha utilizado otra función de mutación (setreg()
). la diferencia es quesetreg()
nunca devuelve nada, lo que significa que siempre devuelve el número0
.Pero cuidado, sobrescribirá su registro
a
. Creo que es un poco más directo que la respuesta de luc, pero tal vez la suya sea más rápida. Si esta solución es de alguna manera peor que la suya, me encantaría escuchar cualquier comentario sobre por qué su respuesta es mejor. ¡Cualquier comentario para mejorar la respuesta será muy apreciado!(También se basa en una respuesta SO tan mía /programming/43539251/how-to-replace-finding-words-with-the-different-in-each-occurrence-in-vi-vim -edi / 43539546 # 43539546 )
fuente
@a+setreg('a',@a+1)
es más corto quelen(add(t,1))
. De lo contrario, este es otro buen truco sucio :). No he pensado en este. Con respecto al uso de una función de mutación del diccionario en el texto de reemplazo, de:s
ysubstitute()
, he notado que esto es mucho más rápido que los bucles explícitos, de ahí la implementación de las funciones de mi lista en lh-vim-lib . Supongo que su solución estará a la par con la mía, puede ser un poco más rápido, no lo sé.@a
modifica. En los guiones, esto es importante para la OMI. Mientras esté en modo interactivo, como usuario final, sabré qué registro puedo usar. Jugar con un registro es menos importante. En mi solución, en modo interactivo, se enreda una variable global; en un script sería una variable local.+3
, podría escribir algo como\=add(thelist, 3 + get(thelist, -1, 0))[-1]
.Encontré una pregunta similar pero diferente que hice hace un par de años y logré cambiar una de sus respuestas sin comprender completamente lo que estaba haciendo y ha funcionado muy bien:
Específicamente, no entiendo por qué el mío no usa
%
o por qué solo uso una variable simple que las otras respuestas evitan por alguna razón.fuente
s//g
declaración normal . De todos modos es una solución interesante. Quizás @LucHermitte pueda darle más información sobre los pros y los contras, ya que mi conocimiento sobre vimscript es bastante limitado en comparación con el suyo.printf()
embargo, ya que las Listas se introdujeron en Vim 7. Pero debo admitir que no habría esperado (¿o no lo recordaba?)<bar>
Pertenecer al alcance de:global
- IOW, el escenario que hubiera esperado era aplicar el:sub
en las líneas coincidentes, luego incrementari
una vez al final. Espero que esta solución sea un poco más lenta. pero, realmente importa? Lo importante es la facilidad con la que podemos encontrar una solución de memoria + prueba y error. Por ejemplo, los Vimgolfers prefieren macros.g/s//
comportamiento del alcance permite otros trucos sucios. Así que gracias a ambos por las interesantes respuestas y la discusión, a menudo no aprendo tanto al dar una respuesta =).Ya hay tres excelentes respuestas en esta página, pero, como sugirió Luc Hermitte en un comentario , si lo hace de manera espontánea, lo importante es qué tan rápido y fácil puede encontrar una solución que funcione.
Como tal, este es un problema que en realidad no usaría
:substitute
para nada: es uno que se puede resolver fácilmente usando comandos normales de modo normal y una macro recursiva:(Si es necesario) Primero, apague
'wrapscan'
. La expresión regular que vamos a utilizar coincidirá con el texto del resultado deseado, así como con el texto inicial, por lo que con'wrapscan'
on, la macro continuaría reproduciéndose para siempre. (O hasta que se dé cuenta de lo que está sucediendo y presione<C-C>
):Configure su término de búsqueda (usando la misma expresión regular base ya mencionada en las respuestas existentes):
(Si es necesario) Vuelva al primer partido presionando
N
tantas veces como sea necesario,(Si es necesario) Cambie la primera coincidencia en el texto deseado:
Borre el
"q
registro y comience a grabar una macro:Yank el contador actual:
Salta al siguiente partido:
Reemplace el contador actual con el que acabamos de extraer:
Incrementar el contador:
Jugar macro
q
. El registro"q
todavía está vacío porque lo borramos en el paso 5, por lo que no sucede nada en este punto:Deja de grabar la macro:
¡Juega la nueva macro y mira!
Al igual que con todas las macros, esto se parece a muchos pasos cuando se explica como lo he hecho anteriormente, pero tenga en cuenta que escribir esto realmente es muy rápido para mí: aparte de la placa repetitiva de grabación de macro, todos son los normales. comandos de edición que realizo todo el tiempo durante la edición. El único paso en el que tenía que hacer algo, incluso acercarme al pensamiento, era el paso 2, donde escribía la expresión regular para realizar la búsqueda.
Formateada como dos comandos de modo de línea de comandos y una serie de pulsaciones de teclas, la velocidad de este tipo de solución se vuelve más clara: puedo conjurar lo siguiente casi tan rápido como puedo escribirlo 1 :
Probablemente podría haber encontrado las otras soluciones en esta página con un poco de reflexión y algunas referencias a la documentación 2 , pero, una vez que comprenda cómo funcionan las macros, son realmente fáciles de producir a la velocidad que normalmente edita.
1: No son situaciones en las que las macros requieren mayor reflexión, pero me parece que no suben mucho en la práctica. Y, en general, las situaciones en las que ocurren son aquellas en las que una macro es la única solución práctica.
2: No implica que los otros respondedores no podrían haber presentado sus soluciones de manera similar fácilmente: solo requieren habilidades / conocimientos que personalmente no tengo tan fácilmente a mi alcance. ¡Pero todos los usuarios de Vim saben cómo usar comandos de edición regulares!
fuente