Regex 用递增的数字替换每个匹配项

Regex 用递增的数字替换每个匹配项,regex,perl,vim,Regex,Perl,Vim,我需要在JSON文件中的对象列表中添加一个ID属性。我发现了有用的内联Perl脚本,它将每个匹配项替换为一个随机整数 :%! perl -pne 's/XYZ/int(rand 1000)/ge' 假设我事先不知道列表的长度,我将如何替换这样的内容 { "id" : XYZ }, { "id" : XYZ }, { ... } 为此: { "id" : 1 }, { "id" : 2 }, { ... } 我试过这个: :%! perl -pne 's/XY

我需要在JSON文件中的对象列表中添加一个ID属性。我发现了有用的内联Perl脚本,它将每个匹配项替换为一个随机整数

:%! perl -pne 's/XYZ/int(rand 1000)/ge'
假设我事先不知道列表的长度,我将如何替换这样的内容

{
    "id" : XYZ
},
{
    "id" : XYZ
},
{ ... }
为此:

{
    "id" : 1
},
{
    "id" : 2
},
{ ... }
我试过这个:

:%! perl -pne 's/XYZ/(0..100)/ge'
但这是假设我提前知道列表的长度,并且也不能像预期的那样工作。我知道我可以用这样的方法获得许多匹配项:

:%s/"id" : XYZ//gn

-p
已经逐行处理输入,因此
-n
是冗余的

perl -pe 's/XYZ/$i++/ge'
$i++
在数字上下文中返回
$i
的值,并向其添加1。

使用Perl(在5.14+中),您不需要处理可能出现在其他地方的字符串,不管字符串和数字是否应该被引用,或者其他复杂的问题,您只需将JSON作为数据结构进行修改即可(可能需要修改代码以修改完整JSON结构的正确部分):

更好的方法是使用,因为默认情况下安装和使用更快的解析器


-0777
开关是一个用于一行程序的开关,用于使readline或
操作符(也由-p开关使用)立即返回整个输入。

如果您在Vim中执行此操作,并且您有
+perl
那么:

:%perldo s/XYZ/++$i/e
如果您想使用普通的Vim命令来执行,您可以按照 使用相同的逻辑和变量。但是,它需要 预先创建,每次替换后递增。 因此,您可能需要
:g
的帮助来执行两个命令:

:let i=0
:g/XYZ/let i+=1|s//\=i
空的
s/
重用
:g
中的模式

或者,只需像您所做的那样通过Perl对其进行过滤 原来:

:%!perl -pe 's/XYZ/++$i/e'

对于那些寻找纯Vim解决方案的人,这里有一个比@sidyll的答案更规范、更一般的解决方案,它不需要
:perldo
:global
,也可以在一行中使用多个匹配项(将
g
标志添加到
:substitute
):

内置替代品 在Vim中,您可以使用任意表达式替换文本。不幸的是,变量递增在Vim中是一个语句,而不是一个表达式。任何一个都必须使用一些技巧,例如使用列表的长度作为计数器(
add()
可以在表达式中使用):

或者您必须定义一个单独的函数(一次):

带插件 表达式语法有点奇怪(但是如果你喜欢Perl,你习惯了更糟糕的情况:-)很麻烦。My通过几个变体改进了内置的
:substitute
命令。其中
:SubstituteExecute
,它得益于预定义的上下文对象:

:%SubstituteExecute /XYZ/ let v:val.n += 1 | return v:val.n
重新编号是如此频繁的任务,以至于插件甚至有一个特殊的命令:

:Renumber /XYZ/
警告
  • 结构化但灵活的语法(如HTML或JSON)不适合使用正则表达式进行解析,除非您确信所使用的允许语法子集是非常规则的(例如,因为您知道输入源和/或通过漂亮的打印机传递了输入)。与通用工具相比,您更喜欢专用工具(即,对于XML使用
    xmlstarlet
    ,对于JSON使用
    jq
    ,而不是
    grep
    sed

OP似乎想在1开始计数器,所以为什么不
+$i
?@zdim:我不确定OP想在哪里开始:
0..100
似乎在0开始。啊,对了……我正在查看所需的输出。不管怎样,adetail@ikegami:我测试了代码,它成功了。正在回滚。是的,++在undef上有特殊行为特别是为了优化这种情况:
undef始终被视为数字,特别是在递增之前被更改为0(这样一个undef值的递增后将返回0而不是undef).
遗憾的是,没有使用
+perl
编译Vim,但是由于您的第三个建议与@choroba相同,所以请进行投票s@D_________
:perldo
只是基本过滤示例的一个快速替代品,但是这种集成涉及到更多的优秀功能我强烈建议您使用
+perl
Vim并查看这些命令的文档
function! Increment()
    let g:i += 1
    return g:i
endfunction
let g:i = 0 | %substitute/XYZ/\=Increment()/
:%SubstituteExecute /XYZ/ let v:val.n += 1 | return v:val.n
:Renumber /XYZ/