从范围内进行多行 Vim 正则表达式替换

3

我尝试使用vim将分层的(xml)文件重新格式化为“每行”文件。

这是一个简化的例子。实际情况非常“大”(500k行),条目和组是任意数量。

输入文件:

<group key="abc">
  <entry val="1"/>
  <entry val="2"/>
  <entry val="3"/>
</group>
<group key="xyz">
  <entry val="1"/>
  <entry val="2"/>
  <entry val="3"/>
  <entry val="4"/>
  <entry val="5"/>
</group>

输出结果:

abc,1
abc,2
abc,3
xyz,1
xyz,2
xyz,3
xyz,4
xyz,5

请注意,我并不需要一个能够完成所有工作的神奇表达式(尽管这会很不错)。我遇到的问题是如何获取与每个条目关联的键。我相信有一个良好的习惯用法来处理这个问题。提前感谢您的帮助。
我尝试过一件事情,可能对其他人有用,具体如下:
:g/key="\(.*\)"/.;/<\/group/s/<entry /\1,<entry /g

这段代码无法正常工作的原因是匹配范围没有传递到替换部分。该表达式实际上是在寻找pat1,在那里建立一个范围,从pat2到pat3,然后用pat4替换pat3(但仅限于pat1和pat2之间的实例)。

:g/pat1/.;/pat2/s/pat3/pat4/g

解决方案

以下是最佳解决方案,通过查找条目并向后查找键来解决了问题,而不是像我之前尝试的那样构建范围和多个替换。最终有效的方法需要进行一些小修改,因此在此提供给其他人。执行重要任务的命令如下:

:g/entry/?key?,\?t.
:g/entry/norm ddpkJ
:v/entry/d

解析:

查找所有的输入行:

:g/entry/

从那里开始向后搜索,找到包含关键字的行,并将其复制到每个条目下面。
?key?,\?t.

搜索所有记录,并切换到普通模式编辑。
:g/entry/norm

交换这两行(删除关键行并将其粘贴到组行下面)。移到关键行并连接这两行。

ddpkJ

一旦所有键都映射完成,搜索没有条目的任何行并删除它们。
:v/entry/d

如果你像我一样有多个层次结构,你可以多次运行前两行代码。一旦所有内容都在单行上,就很容易将其清理成所需的最终格式。另一个主要优点是,这种解决方案可以轻松地放入脚本中,并进行重新运行。

vim -S script.vim data.file

我发现一个非常好的技巧,几乎可以让我开始使用这个表达式::g/<group key="(.*)"/.;/</group/s/<entry /\1, <entry /g它搜索pat1,从“here”到pat2构建范围,然后进行替换。:g/pat1/.;/pat2/s/abc/def/g但是,你不能将pat1的匹配结果带入到替换中。无论如何,我不知道那个习惯用法,所以想分享一下。 - JHiant
只需将您尝试过的内容放在问题中即可。(格式化可以使其看起来更好) - FDinoff
谢谢@FDinoff。完成。(第一篇帖子) - JHiant
2个回答

1

好的,这不是一个神奇的一行代码,但可能有效:

ggqq/groupf"lyi"<c-v>n0I<c-r>"<esc>ddnddq
100@q
:%s/\s*<entry val="/,/g
:%s/"\/>//g

逐步操作:
gg       => Go to the top
qq       => Record a macro called q
/group   => Search for "group"
f"l      => Go to the key
yi"      => Copy the key
c-v      => Vertical visual mode
n0       => Go to the end of the "group", place the cursor at the beginning
I<c-r>"<esc> => Paste at the beginning
dd       => Delete <group> line
ndd      => Delete end </group> line
q        => Stop macro

100@q    => Play macro 100 times, use whatever you need

现在你应该有类似这样的东西:
abc  <entry val="1"/>
abc  <entry val="2"/>
abc  <entry val="3"/>
xyz  <entry val="1"/>
xyz  <entry val="2"/>
xyz  <entry val="3"/>
xyz  <entry val="4"/>
xyz  <entry val="5"/>

然后只需清除您不需要的部分:
:%s/\s*<entry val="/,/g
:%s/"\/>//g

感谢您提供的解决方案。QQ宏惯用语是我已经有一段时间没有使用过了。对于中小型文件,这很有效。但是,对于非常大的文件,我需要在vim之外进行大量的行计数工作,以确定要重放宏的次数。 - JHiant

1
以下内容可以正常工作。
:g/entry/?<group?,?<group?t.
:%norm J
:g/<\//d
:%norm df"f"df"i,<C-v><Esc>f"d$

分解

对于每一行包含entry的行,向后搜索<group并将其复制到entry下面的行。

:g/entry/?<group?,?<group?t.

<group key="abc">
  <entry val="1"/>
<group key="abc">
  <entry val="2"/>
<group key="abc">
  <entry val="3"/>
<group key="abc">
</group>
<group key="xyz">
  <entry val="1"/>
<group key="xyz">
  <entry val="2"/>
<group key="xyz">
  <entry val="3"/>
<group key="xyz">
  <entry val="4"/>
<group key="xyz">
  <entry val="5"/>
<group key="xyz">
</group>

连接所有行

:%norm J

<group key="abc"> <entry val="1"/>
<group key="abc"> <entry val="2"/>
<group key="abc"> <entry val="3"/>
<group key="abc"> </group>
<group key="xyz"> <entry val="1"/>
<group key="xyz"> <entry val="2"/>
<group key="xyz"> <entry val="3"/>
<group key="xyz"> <entry val="4"/>
<group key="xyz"> <entry val="5"/>
<group key="xyz"> </group>

删除闭合标签

:g/<\//d

<group key="abc"> <entry val="1"/>
<group key="abc"> <entry val="2"/>
<group key="abc"> <entry val="3"/>
<group key="xyz"> <entry val="1"/>
<group key="xyz"> <entry val="2"/>
<group key="xyz"> <entry val="3"/>
<group key="xyz"> <entry val="4"/>
<group key="xyz"> <entry val="5"/>

通过搜索和删除引号内的文本来修正剩余的文本。请注意,<C-v><Esc> 是添加转义符到命令中的键序列。
:%norm df"f"df"i,<C-v><Esc>f"d$

abc,1
abc,2
abc,3
xyz,1
xyz,2
xyz,3
xyz,4
xyz,5

你能解释一下 ?pat?, ?pat?t 这个范围表达式吗? - JHiant
不使用依赖于文件完美平衡的:%norm J命令,而是使用:g/entry/norm kJ命令来查找所有entry行,并移动到它们上方的已粘贴组合行,然后将它们合并。这种方法支持多个级别。 - JHiant
最佳答案,因为它巧妙地反转了我正在进行的搜索替换。这对于存在额外层次结构的情况也非常有效。一旦我在一行上获得了所有需要的数据,只需使用“:%grep entry”清除其他所有内容。然后就是 %s/junk/,/ 直到完成。谢谢! - JHiant
欢迎。如果您仍需要“pat”范围表达式的解释,它只是从当前选定行(在我们的情况下是一个条目行)向后搜索“组”行,并将范围设置为1行(我认为可以缩短但尚未尝试)。 “t”将范围(我们的一行)复制到当前行下面。 - Lieven Keersmaekers
谢谢。我不知道:t命令。它与:copy同义。范围可以缩短为:?pat?,?其中?搜索先前使用的搜索模式匹配的上一行。 - JHiant

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