如何在vim中使用正则表达式替换为递增的数字变量

8

我有一个文件,内容如下:

id      phone   name
x'1234' 12345   jack
x'4567' 45678   Jojo
x'7890'  89456  Dio
x'4591'  34872  joseph

我想把它解析成以下格式:
id  phone   name
1   12345   jack
2   45678   Jojo
3    89456  Dio
4    34872  joseph

我知道基本的正则表达式可以将所有的ID替换为任何字符串,像这样:

:%s/x'\(\w\+\)'/1/g

然后它将变成:

id  phone   name
1   12345   jack
1   45678   Jojo
1    89456  Dio
1    34872  joseph

如何将 id 替换为递增变量?

你是在问关于vi还是vim? - melpomene
是的,我想将ID更改为递增数字。 - a4fz067lu
那不是一个是/否的问题。 - melpomene
参见 https://stackoverflow.com/a/54383607/4400820 - D. Ben Knoble
5个回答

12

请注意,在替换命令(:s)中,您可以使用表达式作为替换字符串。当替换字符串以\=开头时,它将被视为表达式进行评估。

这里,一个可能的解决方案是

:let i=1 | g/^x'\d\+'/s//\=i/ | let i=i+1

它查找所有出现的^x'\d\+'模式(每行一个),并用值替换它,如果找到匹配,则递增变量i。正如在评论中所指出的一样,|g替换“code”的一部分,因为|用于一次执行多个命令”另一个解决方案是使用line()命令(考虑到您的文件有一个标题顶部行,因此您实际上应该从line()返回的值中减去1):
%s/^x'\d\+'/\=line('.')-1/
^x'\d\+'正则表达式匹配:
  • ^ - 行首
  • x' - x' 字符串
  • \d\+ - 至少 1 个数字
  • ' - 一个 ' 字符。
还有其他有趣的“在正则表达式中递增数字”的示例,请参见在替换命令中使用表达式页面:
  • 对文件中的所有行进行编号(插入行号,后跟制表符):
    :%s/^/\=line('.')."\t"/
  • 对行范围进行编号(从第 10 行到第 20 行):
    :10,20s/^/\=line('.')."\t"/
  • 对一系列行进行编号,从 1 开始递增:
    :let counter=0|10,20g//let counter=counter+1|s/^/\=counter."\t"
  • 对范围内的所有段落进行编号,从 1 开始递增(假设段落之间由一个或多个空行分隔):
    :let counter=0|1,20g/^$\n^\s*[^\s]/let counter=counter+1|+1s/^/\=counter."\t"
    注意:如果文件中的第一个段落上面没有空行,则上述命令不起作用。

1
这是一个非常好的答案。但其中真正有趣的部分是 | let i=i+1g// 命令的一部分,因此对这些行中的每一行都执行。从视觉上看,乍一看并不像那样。解释一下这个部分会改善这个答案。 - filbranden
哦,谢谢您的指导和建议。 - a4fz067lu

8

一次性执行

您可以像其他答案所示那样声明变量,或者:

:%s/^x'\([^']*\)'/\=line('.')-1/  

如果标题行(ID、电话号码、姓名)不在第一行,请将最后一个1替换为标题行的行号。

两个步骤

只需将代码中的1替换为0,即可得到以下结果:

id  phone   name
0   12345   jack
0   45678   Jojo
0    89456  Dio
0    34872  joseph

然后将光标移动到第一个id0,按下:

ctrl-v G g ctrl-a

这将把0转换为从1开始的序列。

  • ctrl-v:列模式
  • G:选择第一列至最后一行。
  • g+ctrl-a:添加。

这很有趣 - a4fz067lu

1

我的PatternsOnText插件中有一个(除了其他很多):Renumber命令用于此操作;它简化了使用:substitute替换表达式的典型解决方案:

:%Renumber/^x'\d\+'/

1

或者你可以使用 awkcolumn 和 Vim 的过滤命令。

:%!gawk 'NR>1 {$1=++i} 1' | column -t

解释:

  • :%!{cmd} 将范围 %(整个文件)通过命令 {cmd} 进行“过滤”
  • gawk '..' 将运行一个 awk 一行命令
  • NR>1 {..} 将对记录编号大于 1 的“块”进行运行(跳过标题)
  • $1=++i 将第一个字段设置为变量 i(预增加)
  • 1 等同于 {print},将输出该行
  • {cmd1} | {cmd2} 将从命令 {cmd1} 输出的内容传递到命令 {cmd2} 的输入中
  • column -t 将以漂亮的表格形式打印数据

0

很好的答案,我用来枚举大型protobuf文件的一个技巧:

首先,将目标搜索/替换为像%%%%这样的唯一字符串,然后使用以下命令: :let i=1 | g/%%%%/s//\=i/ | let i = i+1

例如,将 [ProtoMember()] [ProtoMember()] [ProtoMember()] 替换为: [ProtoMember(1)] [ProtoMember(2)] [ProtoMember(3)]

首先将所有内容变成[ProtoMember(%%%%)]

:% s/Member()/Member(%%%%)/g

然后使用

:let i=1 | g/%%%%/s//\=i/ | let i = i+1


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