Vim宏或插件用于顺序重新编号XML元素?

5
我有多个XML文件,每天需要编辑。我无法控制读取这些文件的源,也无法控制XML的格式。
问题在于,每个元素都需要在每个块内拥有自己的顺序编号。有时每个块内可能有多达200个元素。当我需要在现有的长块中插入一个命令时,我必须手动重新编号每个后续元素,这很繁琐且容易出错。
此外,名称/值对与所需的顺序编号之间没有任何连接。下面的值"origcmd1"可以是"foobar98765"。它们只是为了说明我的问题而进行编号。
例如:
从这里开始:
<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>

如果我需要在origcmd1和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>

我现在必须手动重新编号cmd4和cmd5。当有数百个命令时,这变得非常令人沮丧。
目前的解决方案包括尝试编写宏以从第一行开始重新编号,假设它总是正确编号为1。然后我将使用一系列的删除/粘贴和Ctrl-a来增加每个后续行号。不幸的是,我无法使宏正常工作。
我还查看了vim.org的vim插件,但我没有发现我认为是解决方案的插件。
Vim对我来说很新,但我喜欢它,而且这似乎是它擅长解决的问题。任何关于快速技术或我错过的插件的想法都会受到赞赏。
2个回答

4
以下内容对我来说似乎有效:

以下是我使用的方法:

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()

为了使其正常工作,您应该在要修复的块内部。 只需在正常模式下键入“,fb”,即可完成此操作。


谢谢,skeept。 它“几乎”可以工作了,我正在努力理解vimscript中发生的事情。唯一的问题是当参数中有空格时。很抱歉我没有指定这个问题,我甚至没有想过这可能是一个问题。 当参数中有空格时,这个: <cmd2 name="schoolCount">ls -l,grep -c teacher</cmd2> 变成了: <cmd2 teacher</cmd2> 除此之外,一切似乎比之前好得多了。 (编辑时弄清楚了如何在注释中格式化代码) - Tye
我已经修复了代码以处理你的示例。如果现在可以,请告诉我它是否有效(我已经用“”注释掉了旧行,这样你就可以看到差异了)。 - skeept

2
如果XML格式比较固定,您确实可以使用(稍微复杂的)Vim宏来操作内容,但要注意XML格式中微小的变化(或注释块)可能会破坏并损坏您的数据。由于Vim作为通用文本编辑器对XML结构没有真正的理解,因此很难使宏更加健壮。
像XSLT这样的XML处理器可能更适合这项工作。(虽然对我而言,编写XSLT转换比记录Vim宏更难!但如果您经常需要这样做,这可能是值得投资的。)您甚至可以从Vim内部调用它::%!xsltproc ... 示例XSLT 1.0样式表...
<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>

这是一个更好的方法。如果我在你的答案中添加一个XSLT示例,你介意吗?如果你愿意,我也可以添加自己的答案。 - Daniel Haley
@DanielHaley:当然,继续吧!有一个简短的例子来说明这一点会很好。 - Ingo Karkat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接