Pregunta ¿Vim macro o complemento para renumerar secuencialmente elementos xml?


Tengo varios archivos XML que necesito editar todos los días. No tengo control sobre la fuente que lee estos archivos ni control sobre el formato del XML.

El problema es que cada elemento necesita su propio número secuencial dentro de cada bloque. A veces puede haber más de 200 elementos dentro de cada bloque. Cuando necesito insertar un comando al principio de un bloque largo existente, debo volver a numerar manualmente cada elemento siguiente que es tedioso y propenso a errores.

Además, los pares nombre / valor no tienen conexión con la numeración secuencial necesaria. El valor "origcmd1" a continuación podría haber sido "foobar98765". Son números solo para ilustrar mi problema.

Ejemplo:

Comenzando con esto:

<block1>
    <cmd1 name="origcmd1"></cmd1>
    <cmd2 name="origcmd2">someCmdsTakeParams,param2</cmd2>
    <cmd3 name="origcmd3"></cmd3>
</block1>

<block3>                             <c>no guarantee blocks are sequential #</c>
    <cmd1 name="cmd1"></cmd1>
    <cmd2 name="cmd2"></cmd2>
    <cmd3 name="cmd3"></cmd3>
</block3>

Si necesito agregar un comando antes, diga entre origcmd1 y origcmd2:

<block1>
    <cmd1 name="origcmd1"></cmd1>
    <cmd2 name="NEWcmd1"></cmd2>                    <c>cmd2 & cmd3 inserted</c>
    <cmd3 name="NEWcmd1"></cmd3>
    <cmd4 name="origcmd2">someCmdsTakeParams,param2</cmd4>
    <cmd5 name="origcmd3"></cmd5>
</block1>

<block3>                             <c>no guarantee blocks are sequential #</c>
    <cmd1 name="cmd1"></cmd1>
    <cmd2 name="cmd2"></cmd2>
    <cmd3 name="cmd3"></cmd3>
</block3>

Ahora debo pasar y volver a numerar manualmente lo que ahora son cmd4 y cmd5. Cuando hay cientos de comandos, esto se vuelve muy frustrante.

Las soluciones hasta ahora han incluido el intento de escribir una macro para renumerar desde la primera línea, asumiendo que siempre está numerada correctamente como 1. Luego usaría una serie de eliminar / pegar y Ctrl-a para incrementar cada número de línea de procedimiento. Desafortunadamente, nunca pude hacer que la macro funcionara correctamente.

También revisé los complementos de vim en vim.org pero no encontré nada que reconociera como una solución.

Vim es nuevo para mí, pero me ha gustado y este parece ser el tipo de problema que está bien adaptado para resolver. Cualquier idea sobre una técnica rápida o un plugin que perdí es apreciada.


5
2017-12-07 00:13


origen


Respuestas:


Lo siguiente parece funcionar para mí:

function! FixBlock()
  let g:pos_end = search("<\/bloc")
  call search("<block", "be")
  let s:i = 0
  while getpos(".")[1] < g:pos_end
    call search("cmd", "e")
    let s:i = s:i + 1
    exe 's/^\(.*\)\(<cmd[^ ]*\) \(.*\)/\1cmd' . s:i . ' \3/'
    "exe 's/^\(.*\)\(cmd.*\) \(.*\)/\1cmd' . s:i . ' \3/'
    exe 's?\(.*\)\(</cmd.*\)>\(.*\)?\1</cmd' . s:i . '>\3'
    normal j0
  endwhile
endfunction

map ,fb :call FixBlock()

para que funcione, debes estar dentro del bloque que quieres arreglar. Solo escribe, fb en modo normal y eso debería hacer el trabajo.


4
2017-12-07 05:40



Si el formato del XML es bastante fijo, puede usar macros Vim (algo complejas) para manipular el contenido, pero tenga en cuenta que los cambios sutiles en el formato XML (o bloques comentados) pueden causar estragos y dañar sus datos. Como Vim, como editor de texto general, no tiene una comprensión real de la estructura XML, es difícil hacer que la macro sea más robusta.

Un procesador XML como XSLT Puede ser más adecuado para el trabajo. (Aunque para mí, es más difícil escribir una transformación en XSLT que grabar una macro de Vim. Pero si la necesita con frecuencia, puede ser una inversión que valga la pena). Incluso puede invocarla desde dentro de Vim: :%!xsltproc ...

Ejemplo XSLT 1.0 Hoja de estilo ...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[starts-with(name(),'cmd')]">
        <xsl:element name="cmd{position()}">
            <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

2
2017-12-07 07:51



Preguntas populares