我想搜索某个特定模式的每次出现,并将其替换为以1开头的十进制数,并为每个匹配项增加一个。

我可以找到措辞类似的问题,这些问题原来与递增无关计数器,但将每个匹配项修改为固定值。其他类似的问题与插入行号而不是递增计数器有关。

示例,之前:

#1
#1.25
#1.5
#2


之后:

#1
#2
#3
#4


我的真实数据周围有很多要重新编号的文字。

评论

如果您有perldo,可以使用:%perldo s /#\ K \ d +(\。\ d +)?/ ++ $ i / ge

@Sundeep:我应该提到我在香草Windows 10上,对不起!

#1 楼

 :let @a=1 | %s/search/\='replace'.(@a+setreg('a',@a+1))/g


但是请注意,它将覆盖您的寄存器a
我认为它比luc的回答更直接一些,但也许他更快。如果此解决方案比他的解决方案差,我很想听听任何反馈,以使他的回答更好。

(它也是基于我的SO答案https://stackoverflow.com/questions/43539251/how-to-replace-finding- vim中每个词的出现次数不同,edi / 43539546#43539546)

评论


我看不到@ a + setreg('a',@ a + 1)比len(add(t,1))短。否则,这是一个很好的技巧:)。我还没有这个。关于在:s和替代()的替换文本中使用字典变异函数,我注意到这比显式循环快得多-因此我的列表函数在lh-vim-lib中实现。我猜您的解决方案将与我的解决方案相提并论,可能会更快一些,我不知道。

–卢克·赫米特(Luc Hermitte)
17年7月10日在11:02

关于首选项,我更喜欢我的解决方案,原因有一个:保留@a不变。在脚本中,这是重要的IMO。在交互模式下,作为最终用户,我将知道可以使用哪个寄存器。弄乱寄存器不太重要。在我的解决方案中,在交互模式下,混乱了一个全局变量;在脚本中,它将是局部变量。

–卢克·赫米特(Luc Hermitte)
17年7月10日在11:05

@LucHermitte对不起,我的解决方案的确比您的解决方案短,在写这样的声明之前,我应该更好地阅读它。我已从回答中删除了该声明,并向您致歉!感谢您提供的有趣反馈,我非常感谢。

– Doktor OSwaldo
17年7月10日在11:07

不用担心由于使用了正则表达式,因此很容易想到要输入的内容很多。另外,我自愿承认我的解决方案很复杂。欢迎您提供反馈。 :)

–卢克·赫米特(Luc Hermitte)
17年7月10日在11:09

的确,你是严厉的。大多数时候,我会提取存储在数组最后位置的另一信息,这是我最后插入的信息(最后一个元素)。例如,对于+3,我可以写类似\ = add(thelist,3 + get(thelist,-1,0))[-1]。

–卢克·赫米特(Luc Hermitte)
17年7月10日在11:16

#2 楼

您需要用状态替换。我记得为SO上的此类问题提供了一个(/几个)完整的解决方案。

这里是另一种进行方法(1)。
现在,我将分2个步骤进行操作:


我需要一个虚拟列表变量来处理肮脏而令人费解的把戏
插入我要在每次匹配出现时填充的该虚拟数组的len。

给出以下内容:

:let t=[]
:%s/#\zs\d\+\(\.\d\+\)\=\ze/\=len(add(t,1))/g


如果不使用对于vim正则表达式,我使用:h /\zs\ze来指定我要匹配的子模式,然后匹配一系列可能在其后跟一个点和其他数字的数字。对于任何浮点数来说,这都不是完美的选择,但这在这里就足够了。

注意:您必须将其包装在一对函数+命令中,以实现简单的界面。再次,有一些关于SO / vim的示例(在这里,在这里,在这里)
如今,我对vim的了解已经足够多了,不必在意将此技巧包装到命令中。确实,我将能够在第一次尝试时编写此命令,而我将花几分钟记住该命令的名称。


(1)目的是能够维持替换之间的状态,并用依赖于当前状态的内容替换当前出现的内容。

感谢:s\=,我们能够插入计算结果。

仍然是国家的问题。我们要么定义一个管理外部状态的函数,要么更新自己的外部状态。在C语言(和相关语言)中,我们可能使用过length++length+=1之类的东西。不幸的是,在vim脚本中,无法立即使用+=。它需要与:set:let一起使用。这意味着:let length+=1会增加一个数字,但不会返回任何值。我们不能写:s/pattern/\=(length+=1)。我们还需要其他东西。

我们需要变异函数。即使他们的输入突变的功能。我们有setreg()map()add()甚至更多。让我们从它们开始。


setreg()突变了一个寄存器。完善。我们可以像在@Doktor OSwaldo的解决方案中那样编写setreg('a',@a+1)。但是,这还不够。 setreg()不仅仅是一个过程,而不仅仅是一个函数(对于我们当中认识Pascal,Ada ...的人们来说)。这意味着它不返回任何东西。实际上,它确实返回了一些东西。标称出口(即非例外出口)总是返回某些内容。默认情况下,当我们忘记返回某些内容时,将返回0 -它也适用于内置函数。这就是为什么在他的解决方案中替换表达式实际上是\=@a+setreg(...)的原因。整,不是吗?
map()也可以使用。如果我们从一个单数为0(:let single_length=[0])的列表开始,则可以借助map(single_length, 'v:val + 1')将其递增。然后,我们需要返回新的长度。与setreg()不同,map()返回其变异的输入。完美,长度存储在列表的第一个(也是唯一的,因此也是最后一个)位置。替换表达式可以是\=map(...)[0]
add()是我经常不习惯使用的表达式(尽管我实际上已经了解了map(),但我还没有将他们各自的表现作为基准)。 add()的想法是使用列表作为当前状态,并在每次替换之前在末尾附加一些内容。我经常将新值存储在列表的末尾,并将其用于替换。由于add()也返回其变异的输入列表,因此我们可以使用:\=add(state, Func(state[-1], submatch(0)))[-1]。在OP的情况下,我们只需要记住到目前为止已检测到多少个匹配项即可。返回此状态列表的长度就足够了。因此是我的\=len(add(state, whatever))


评论


我想我理解这一点,但是为什么将数组及其技巧与将一个变量加到变量上相比却有些技巧呢?

–hippietrail
17年7月10日在13:41

这是因为\ =需要一个表达式,并且因为与C不同,i + = 1不会增加并返回一个表达式。这意味着在\ =后面,我需要一些可以修改计数器并返回表达式(等于该计数器)的东西。到目前为止,我发现的唯一内容就是列表(和字典)操纵函数。 @Doktor OSwaldo使用了另一个变异函数(setreg())。区别在于setreg()从不返回任何内容,这意味着它始终返回数字0。

–卢克·赫米特(Luc Hermitte)
17年7月10日在15:09

哇有趣!你的把戏和他的魔术都很神奇,我认为你的答案将从解释你的答案中受益。我认为只有最流利的vim编写者才会知道这种不直观的习惯用法。

–hippietrail
17年7月11日在3:42

@hippietrail。添加了说明。如果需要更具体的精度,请告诉我。

–卢克·赫米特(Luc Hermitte)
17年7月13日在11:46

#3 楼

几年前,我发现了一个类似但不同的问题,并设法在不完全了解我在做什么的情况下更改了其中一个答案,而且效果一直很好:

:let i = 1 | g/#\d\+\(\.\d\+\)\=/s//\=printf("#%d", i)/ | let i = i+1


特别是我不明白为什么我的不使用%或为什么我只使用其他答案出于某种原因而避免使用的普通变量。

评论


这也是一种可能性。我认为这里的主要缺点是,每场比赛只能使用一个替换命令。因此它可能更慢。我们不使用普通变量的原因是,它将不会在普通的s // g语句中进行更新。无论如何,这是一个有趣的解决方案。也许@LucHermitte可以告诉您更多有关优缺点的信息,因为与他相比,我对vimscript的了解非常有限。

– Doktor OSwaldo
17年7月10日在14:36



@DoktorOSwaldo。我想这个解决方案已经工作了更长的时间了-尽管没有printf()-因为Vim 7中引入了Lists。但是我必须承认我不会(//不记得吗?)属于:global-IOW的范围,我期望的情况是在匹配的行上应用:sub,然后在最后增加i一次。我希望此解决方案会稍微慢一些。但这真的重要吗?重要的是我们能够轻松地从内存+试用和错误中获得一个可行的解决方案。例如,Vimgolfers喜欢使用宏。

–卢克·赫米特(Luc Hermitte)
17年7月10日在16:51



@LucHermitte是的,我ecpexted一样,没有速度没关系。我认为这是一个很好的答案,我再次从中学到了一些东西。也许g / s //范围行为允许其他肮脏的把戏。因此,感谢你们提供了有趣的答案和讨论,我从提供答案=)中学到的并不多。

– Doktor OSwaldo
17年7月11日在6:03



#4 楼

此页面上已经有三个不错的答案,但是,正如Luc Hermitte在评论中所建议的那样,如果您要进行现成的操作,那么重要的是,您可以快速,轻松地找到一个可行的解决方案。

就这样,这是一个我根本不会真正使用:substitute的问题:使用常规的普通模式命令和递归宏可以很容易地解决这个问题:



(如有必要)首先,关闭'wrapscan'。我们将要使用的正则表达式将匹配所需的结果文本以及初始文本,因此,在启用'wrapscan'的情况下,该宏将永远继续播放。 (或者直到您意识到正在发生的情况,然后按<C-C>。):

:set nowrapscan



设置搜索词(使用与前面已经提到的基本正则表达式相同的现有答案):

/#\d\+\(\.\d\+\)\?<CR>


(如有必要)按要求多次按N跳回到第一场比赛,

(如果必要)将第一个匹配项更改为所需的文本:

cE#1<Esc> 



清除"q寄存器并开始记录宏:

qqqqq



添加当前计数器:

yiW



跳转到下一个匹配项:

n



将当前计数器替换为我们刚想要的计数器:

vEp



递增计数器:

<C-A>



播放宏q。寄存器"q仍然为空,因为我们已在步骤5中将其清除,因此此时什么也没有发生:

@q



停止记录宏:

q



播放新的宏,然后观看!

@q



与所有宏一样,如我上面所做的解释一样,这看起来像很多步骤,但请注意,实际上对我来说键入这些命令非常快:除了递归宏记录样板之外,它们都只是常规的编辑命令我在编辑过程中一直在执行。我什至不得不做任何思考的唯一步骤是步骤2,在该步骤中,我写了正则表达式来执行搜索。

格式化为两个命令行模式命令和一系列击键,这种类型的解决方案的速度变得更加清晰:我可以像输入它一样快地想到以下内容:1:

:set nowrapscan
/#\d\+\(\.\d\+\)\?
cE#1<Esc>qqqqqyiWnvEp<C-A>@qq@q


我可能想出了本页上的其他解决方案都带有一些思想和对文档的一些引用2,但是,一旦您了解了宏的工作原理,它们就很容易以您通常编辑的速度推出。

1 :在某些情况下,宏需要更多的思考,但是我发现它们在实践中并没有太多用。通常情况下,发生宏的情况是唯一可行的解​​决方案。

2:并不意味着其他答复者不可能同样容易地提出其解决方案:他们只需要我个人不容易掌握的技能/知识。但是所有Vim用户都知道如何使用常规编辑命令!

#5 楼

只需在末尾加'#'加上一个计数器'c'

:let c=0 | g/^#\d\+.*/ let c+=1 | s//\='#' . c


全局部分g/^#\d\+.*/ let c+=1允许我们仅在以下行中递增计数器有图案

评论


这不是在hippietrails中回答吗?

– D. Ben Knoble♦
20年4月16日在13:01

在这种情况下,我想我想出了一种做类似事情的捷径,也许发表评论会更好。

– SergioAraujo
20 Apr 16 '13:07