Búsqueda y reemplazo de líneas múltiples

9

Me gustaría realizar una búsqueda y reemplazar en un archivo con 12000 líneas.

Específicamente, si ^ SetFontSize 28existe una aparición después de un ^Hidebloque y antes del siguiente ^Hideo ^Show, cambie 28a 18.

Aquí hay un fragmento del archivo original.

Hide # Gear - Endgame
    ItemLevel >= 77
    Rarity = Magic
    LinkedSockets >= 3
    BaseType "Runic Hatchet"
    SetTextColor 140 190 255 # Magic Item Highlight
    SetFontSize 28

Hide # Gear - Endgame
    ItemLevel >= 77
    Rarity = Magic
    Sockets >= 3
    BaseType "Runic Hatchet"
    SetTextColor 140 190 255 # Magic Item Highlight
    SetFontSize 28

Show # Gear - Endgame
    ItemLevel >= 83
    Rarity = Normal
    Sockets < 3
    BaseType "Tiger Hook"
    SetTextColor 240 240 240 # Normal Item Highlight
    SetBackgroundColor 70 70 70
    SetFontSize 28

El resultado final para uno de los Hidebloques debería verse así:

Hide # Gear - Endgame
    ItemLevel >= 77
    Rarity = Magic
    LinkedSockets >= 3
    BaseType "Runic Hatchet"
    SetTextColor 140 190 255 # Magic Item Highlight
    SetFontSize 18

Reemplazar SetFontSize 28a SetFontSize 18, pero solo si aparece en un ^Hidebloque.

La expresión regular desagradable que probé fue: :%s/^Hide\(.*\)SetFontSize 28$/Hide\1SetFontSize 18/g

Pero se le dijo patrón no encontrado. Avíseme si necesita información adicional o si mi solicitud no está clara.

Chico
fuente
55
¿Cada Hidebloque tiene una SetFontSizelínea (cualquiera que sea el valor)? Si es así, podría usar:%s/Hide\_.\{-\}SetFontSize \zs28/18/
muru
2
@muru whatever be the valuecausaría problemas, su solución solo funciona si cada Hidebloque tiene una SetFontSizelínea y su valor es exactamente 28, de lo contrario, coincide hasta que aparezca 28otro bloque.
dedowsdi

Respuestas:

3

Una forma de resolver esto sería usar :globalun generador que genere una salida de rango.

El uso típico del globalcomando sería

:[range]g/{pattern}/[cmd]

También tiene la opción de hacer que este patrón genere un rango en lugar de una sola coincidencia de línea mediante el uso ,de

:[range]g/{first pattern}/,/{second pattern}/[cmd]

Esto genera un rango que se aplica al comando.

Por su ejemplo el primer patrón se pongan en venta la primera Hideentrada y el segundo patrón es cualquiera Hide, Showo al final del archivo (asumiendo que usted quiere que el último caso Ocultar).

:g/Hide/,/\(Hide\|Show\|\%$\)/s/SetFontSize 28/SetFontSize 18/

La primera expresión regular es simple, /Hide/. La segunda expresión regular contiene algunas partes interesantes.

  • \(y \)crea una agrupación de átomos para que coincida
  • \| es la operación OR
  • \%$ representa el final del archivo

Una vez que hemos definido nuestros rangos, aplicamos el sustituto con un patrón y una cadena como lo haría normalmente.

Tenga en cuenta que la expresión regular utilizada en este ejemplo es muy básica. Deberá asegurarse de que sus identificadores para el principio y el final del rango capturen las áreas correctas.

jecxjo
fuente
3

Parece que tu desagradable expresión regular no era lo bastante desagradable ... :-)

Sección de búsqueda

La búsqueda tendría que cambiarse a esto:

^Hide\(\(\(Show\|Hide\)\@!\_.\)*\)SetFontSize 28

Esto incluye bastantes cosas poco comunes y tantos paréntesis ... Veamos qué tenemos allí:

El Caret ( ^)

El símbolo de intercalación se usa para indicar el comienzo de la línea. Creo que ya estamos familiarizados con este.

Un punto importante, el ^no funciona, excepto como el primer personaje en su patrón. Después se toma al pie de la letra. Para incluir un comienzo de línea dentro de su expresión, debe usar \_^. Sin embargo, en nuestra situación no necesitábamos eso.

(Hay un fenómeno similar con $y \_$)

El primer y último paréntesis ( \( ... \))

El primer y último paréntesis se usan por sí mismos, lo que significa que tomará lo que aparezca dentro y lo establecerá en parámetro \1. Ya lo usó en su propia expresión regular, por lo que supongo que también está familiarizado con este.

El segundo conjunto de paréntesis

Como puede observar, hay un segundo conjunto de paréntesis seguido de un asterisco \( ... \)*. Esto significa que estamos buscando lo que coincida varias veces. Esta es la forma habitual de usar el asterisco, por lo que debe estar familiarizado con él.

El tercer conjunto de paréntesis, OR, y \_.

Sí, en realidad hay tres paréntesis antes de la palabra Show. Este último conjunto es necesario por dos razones: la \|y la siguiente @!.

Con respecto a la operación OR, ya debería estar familiarizado con ella.

Show\|Hide    or    Hide\|Show

El orden no importa aquí. El \es necesaria frente a la |de trabajo en vim.

El paréntesis alrededor de esta expresión nos permite seguir la expresión por algo . Aquí el @!.

\(Show\|Hide\)@!

Este es mucho menos familiar. Significa si no coincide . Sin embargo, el uso de esto no es muy fácil, pero debe seguir esa expresión con lo que desea extraer que no debe coincidir con dicha expresión. Es por eso que tenemos \_.detrás de ese patrón.

Los \_.medios coinciden con cualquier cosa. Al contrario de lo .que ocurre por sí solo, que no coincide con el \npersonaje. En otras palabras, hacemos coincidir cualquier carácter en cualquier número de líneas a menos que coincida con Showo Hide.

Tenga en cuenta que el paréntesis alrededor de esa expresión también es importante, como lo es el asterisco, por lo que todo esto es realmente lo que lo hace funcionar:

\(\(Show\|Hide\)@!\_.\)*

aka coinciden con lo que sea hasta el siguiente Showo Hidecaracteres (nota que también coincidiría Showing, Shower, HideMe, etc Usted debe ser capaz de utilizar \<y \>si es necesario para que coincida con la palabra exacta.)

Nota al margen: para buscar en varias líneas, también es posible usar el \ncarácter en el patrón. Sin embargo, no es tan versátil como el \_.patrón.

SetFontSize 28

Ahora la sección tiene que incluir SetFontSize 28también. Tal como lo tenías en tu expresión regular. Si no SetFontSize 28aparece en esa sección, intente la búsqueda nuevamente en la siguiente sección.

Debido a la negación anterior (la coincidencia excepto Showo Hide) la búsqueda no se filtra a la siguiente sección, con el riesgo de estropearla.

Sección de reemplazo

El reemplazo es el mismo que tenía:

.../Hide\1SetFontSize 18/

Usamos el paréntesis en la búsqueda para que \1funcione como se esperaba.

Búsqueda completa y reemplazo

Los patrones resultantes son así:

:%s/^Hide\(\(\(Show\|Hide\)@!\_.\)*\)SetFontSize 28/Hide\1SetFontSize 18/

El \(Show\|Hide\)debe incluir todos los encabezados posibles .

Fuentes

Regex para que coincida con cualquier personaje, incluida la nueva línea ( \_.\{-})

Busque líneas que no contengan patrones y otras búsquedas útiles ( @!)

Documentación de Vim: patrón ( \_^)

Alexis Wilke
fuente
1
Me gusta el ()*, mi versión de su respuesta: %s/\v^Hide.*\n(\s+.*\n)*\s*SetFontSize\s+\zs28/16.
dedowsdi