从范围替换多行vim正则表达式

时间:2014-11-04 23:32:27

标签: regex vim multiline

我正在尝试使用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,然后用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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

以下工作

:g/entry/?<group?,?<group?t.
:%norm J
:g/<\//d
:%norm df"f"df"i,<C-v><Esc>f"d$

<强>击穿

对于包含entry的每一行,向后搜索<group并复制到条目下方的行

: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