将每个匹配项替换为递增的数字

时间:2018-10-09 19:04:29

标签: regex perl vim

我需要将ID属性添加到JSON文件中的对象列表中。我发现this有用的内联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

4 个答案:

答案 0 :(得分:4)

如果您在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'

答案 1 :(得分:3)

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

perl -pe 's/XYZ/$i++/ge'

$i++在数字上下文中返回$i的值并将其加1。

答案 2 :(得分:3)

使用Perl的built in JSON parser(在5.14及更高版本中),您无需处理可能会出现在其他地方的字符串(无论是否应使用引号和数字,或其他复杂情况),您只需修改JSON作为数据结构(可能需要更改代码以修改完整JSON结构的正确部分):

perl -0777 -MJSON::PP -E'my $data = decode_json readline; $_->{id} = ++$i foreach @$data; print encode_json $data'

最好使用JSON::MaybeXS,因为它将默认安装并使用更快的解析器。

-0777开关是convention,单线可导致readline或<>运算符(也由-p开关使用)一次返回整个输入。 / p>

答案 3 :(得分:1)

对于那些寻求纯Vim解决方案的人,这是一个比@sidyll的答案更规范和通用的解决方案,它不需要:perldo:global,并且也可以在行(将g标志添加到:substitute):

具有内置替代品

在Vim中,您可以使用:help sub-replace-expression将文本替换为任意表达式。不幸的是,递增变量是Vim中的 statement ,而不是表达式。要么必须采取一些技巧,例如使用List的长度作为计数器(add()可以在表达式中使用):

:let c = [] | %substitute/XYZ/\=len(add(c, 0))/

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

function! Increment()
    let g:i += 1
    return g:i
endfunction
let g:i = 0 | %substitute/XYZ/\=Increment()/

带有插件

表达式语法有点奇怪(但是,如果您对Perl感兴趣的话,您会变得更糟:-)。同样,计数器(和函数)的显式单独初始化也很麻烦。我的PatternsOnText plugin对内置的:substitute命令进行了改进,具有多种变体。其中:SubstituteExecute受益于预定义的上下文对象:

:%SubstituteExecute /XYZ/ let v:val.n += 1 | return v:val.n

重新编号是一项很常见的任务,以至于插件甚至对此都有一个特殊的命令:

:Renumber /XYZ/

注意事项

  • 诸如HTML或JSON之类的结构化但灵活的语法不适合使用正则表达式进行解析,除非您有把握地知道所允许的语法使用的子集非常正规(例如,因为您知道输入源和/或已经通过了通过漂亮的打印机输入)。优先使用专用工具,而不是通用工具(例如,对于XML使用xmlstarlet,对于JSON使用jq,而不是grepsed)。