2017-02-15 127 views
2

我有一些文本,其中有两列和两个或更多的行。第一列名称只打印一次。之后,它在后续行中留空,直到它再次更改。Vim搜索并替换可变行数

例如

name1 xxx 
     zza 
     yyy 
     zzz 
name2 ggg 
     xxx 
name3 xxx 
     zza 
     ggg 
     yyy 
     zzz 

我想更改上述各行打印的列名,如下所示:

name1 xxx 
name1 zza 
name1 yyy 
name1 zzz 
name2 ggg 
name2 xxx 
name3 xxx 
name3 zza 
name3 ggg 
name3 yyy 
name3 zzz 

注:在开始提到的,线条与空白的第一列中的数字是变量。可以是任意数量的上述2

以下方式将工作,如果反复应用:

:%s/\(^\S\+ \)\(.*\n\)\s\+/\1\2\1/ 

不过,当然,这只会更改一行的时间,我必须重复,直到所有比赛已更新。

我的问题:是否有一个命令来完成上述转换的行数是可变的。

+0

而不是'%',您可以指定要操作的行号。 例如:':12,16s/toto/titi/g' – Coconop

回答

1

你的模式几乎是工作,主要的问题是,既然你 更换这改变了线换行,这就是为什么你需要正如你所描述的,运行多次。当你删除下一行时,光标将跳过它(即使它被替换回来),然后你不能匹配。

只需在相关的 部分“开始”匹配,使用\zs标志非常有用,您可以轻松解决此问题。 \v是 也可用于使模式更具可读性,控制魔术 选项。这应该工作:

:%s/^\v(\S+\s+).*\n\zs\s+/\1 

它捕获的非空白部分,移动到下一个空行开始和 并将匹配存在。然后替换捕获的部分。现在当光标移动时,将重复该过程,并且如果以下行 未开始空白(例如在新组的开始处),则 没有任何反应,因为它不匹配。

0

您可以将相同的逻辑记录到宏,在这种情况下q。

qq:%s/\(^\S\+ \)\(.*\n\)\s\+/\1\2\1/按回车键并再次q要停止录制宏

@q将再次运行它,但如果你知道行的最大数量(例如:10),你可以@●运行10

+0

4 @ q将解决您的问题。 1000 @ q这样的高数字也没有什么坏处。它仍然会做你想要的。 – arithran

+1

谢谢。我使用宏来重复发出命令。请参阅sidyll回答一个命令。 – sankoz

0

使用awk这可能是一个解决方案:

:%!awk '{if($2==""){$2=$1;$1=name}else{name=$1};print}' 
0

你可以用这个小Python脚本解决这个问题,提供了所有的空白都是空格,而不是标签:

关口。PY

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 

import fileinput 

for line in fileinput.input(): 
    if not line.startswith(" "): 
      current = line.split()[0] 
      print(line, end="") 
      continue 
    print(line.replace(" " * len(current), current, 1), end="") 

然后通过管道将文本文件的脚本:

python col.py < text.txt 

Vim可以用Python的支持下重新编译,所以这样的事情可能在Vim内部使用。