如何在Vim中的替换命令中重复替换搜索词在一行中出现的次数?

如何在Vim中的替换命令中重复替换搜索词在一行中出现的次数?,vim,replace,Vim,Replace,我想在不使用标签的代码中使用标签。到目前为止,我所做的实现选项卡的工作相当精巧: :%s/^ /\t/g :%s/^\t /\t\t/g . . . 问题:有没有办法用tab(\t)替换两个空格()在行首找到的次数?如果您特别想在行首将空格转换为制表符(反之亦然),有一个有用的:retab命令可以解决这个问题。例如: :重新开始!2将以两个为一组的空格转换为制表符 :设置expandtab,然后:重新设置!2将制表位(宽度为2)转换回空格 有关详细信息,请参见:h:retab(和:h'

我想在不使用标签的代码中使用标签。到目前为止,我所做的实现选项卡的工作相当精巧:

:%s/^  /\t/g
:%s/^\t  /\t\t/g
. . .

问题:有没有办法用tab(
\t
)替换两个空格(
)在行首找到的次数?

如果您特别想在行首将空格转换为制表符(反之亦然),有一个有用的
:retab
命令可以解决这个问题。例如:

  • :重新开始!2
    将以两个为一组的空格转换为制表符
  • :设置expandtab
    ,然后
    :重新设置!2
    将制表位(宽度为2)转换回空格
有关详细信息,请参见
:h:retab
(和
:h'ts'


这不是原始问题的通用解决方案,但我认为它涵盖了最常见的用例。

使用:s regex没有通用的方法。您不能使
/g
修饰符向后看,否则它将不可用,并且如果不向后看,您无法可靠地检查您是否位于行的开头

通常唯一的方法是循环,如下所示:

:for i in range(100)
:   %s/^\t*\zs  /\t/e
:endfor
这是丑陋的,缓慢的和高度不受赞扬的。使用
:retab

有(至少)三种替代技术与此案例相关

1。第一个利用前面的原子匹配 自然定义缩进步骤的语法。根据 问题语句,缩进步骤是一对相邻的空格 开头只有空格的字符 当然可以。根据这个定义,可以构造实际的 替换模式,从右到左:

:%s/\%(^ *\)\@<=  /\t/g
3.第三个想法是通过Vim处理前导空间 脚本语言。一种方便的方法是使用 替换为
:substitute
命令的表达式功能 (请参见
:帮助子替换-\=
)。当以
\=
开头时,替换项 命令字符串允许替换模式的匹配项 对于
\=
之后指定的表达式的求值结果:

:%s#^ \+#\=repeat("\t",len(submatch(0))/2)

这不管用吗<代码>:%s/\t/g?您还可以使用
:retab
命令。说
:设置ts=2 noet
,然后
:重新开始。然后将tabstop重置为您想要使用的值,例如:
:set ts=4
@glts:我会输入此注释作为获取rep的答案(因为这正是OP想要的)@埃克斯:是和否;)我想知道它是如何工作的,并将其用于其他目的。@gits:正如我的问题的注释中所述:这将把所有选项卡都改为空格。那不是我想要的,你说得对。我现在明白你的问题了。现在我也很想看到一个有效的解决方案…@glts:看看我的中列出的命令。谢谢,但正如前面所说的,retab不是一个好的选项,因为它会影响文件中的所有选项卡。如果retab不是您想要的,请使用我提供的for循环替换。正如我所解释的,这确实是只替换行开头的字符的唯一方法。一些解释?谢谢想了更多关于
\%
\@这真是太好了。我不明白为什么
\@@Tom:这是因为在
\@中使用了启发式,如Vim文档中所述(请参见
:help/\@
:%s#^ \+#\=repeat("\t",len(submatch(0))/2)