介绍
在输入底部添加一个整数列表的想法类似于一个著名的数据库黑客(与正则表达式无关),其中一个加入到整数表中。我的原始答案使用了@Qtax技巧。当前的答案使用递归、Qtax技巧(直接或反向变化)或平衡组。
是的,这是可能的……但需要一些警告和正则表达式技巧。
:1:2:3:4:5:6:7
,这是一种类似于使用整数表的著名数据库黑客技巧。输入文件:
假设我们正在搜索pig
并希望用行号替换它。
我们将使用以下内容作为输入:
my cat
dog
my pig
my cow
my mouse
:1:2:3:4:5:6:7
支持的语言:除了上述文本编辑器(Notepad++和EditPad Pro)之外,此解决方案应在使用PCRE(PHP、R、Delphi)、Perl以及使用Matthew Barnett的regex
模块的Python中工作(未经测试)。
递归结构位于前瞻中,是可选的。它的作用是平衡左侧不包含pig
的行与右侧的数字:可以将其视为平衡嵌套结构,例如{{{ }}}
... 除了左侧是无匹配行,右侧是数字。关键在于当我们退出前瞻时,我们知道跳过了多少行。
搜索:
(?sm)(?=.*?pig)(?=((?:^(?:(?!pig)[^\r\n])*(?:\r?\n))(?:(?1)|[^:]+)(:\d+))?).*?\Kpig(?=.*?(?(2)\2):(\d+))
带注释的自由间距版本:
(?xsm) # free-spacing mode, multi-line
(?=.*?pig) # fail right away if pig isn't there
(?= # The Recursive Structure Lives In This Lookahead
( # Group 1
(?: # skip one line
^
(?:(?!pig)[^\r\n])* # zero or more chars not followed by pig
(?:\r?\n) # newline chars
)
(?:(?1)|[^:]+) # recurse Group 1 OR match all chars that are not a :
(:\d+) # match digits
)? # End Group
) # End lookahead.
.*?\Kpig # get to pig
(?=.*?(?(2)\2):(\d+)) # Lookahead: capture the next digits
替换:\3
在演示中,可以看到底部的替换。您可以在前两行上玩弄字母(删除一个空格以制作pig
),将pig
的第一次出现移动到另一行,并查看其如何影响结果。
支持的语言:除了上面提到的文本编辑器(Notepad++和EditPad Pro)外,此解决方案应该适用于使用PCRE(PHP、R、Delphi)的语言,在Perl中以及使用Matthew Barnett的regex
模块的Python中(未经测试)。该解决方案易于通过将\K
转换为前瞻并将占有量词转换为原子组来适应.NET(请参见下面几行的.NET版本)。
搜索:
(?sm)(?=.*?pig)(?:(?:^(?:(?!pig)[^\r\n])*(?:\r?\n))(?=[^:]+((?(1)\1):\d+)))*+.*?\Kpig(?=[^:]+(?(1)\1):(\d+))
.NET版本:回到未来
.NET没有\K
,我们使用一种“回到未来”的正向先行断言(包含一个跳过匹配的前瞻),在它的位置上。此外,我们需要使用原子组而不是贪婪量词。
(?sm)(?<=(?=.*?pig)(?=(?>(?:^(?:(?!pig)[^\r\n])*(?:\r?\n))(?=[^:]+((?(1)\1):\d+)))*).*)pig(?=[^:]+(?(1)\1):(\d+))
带有注释的自由空间版本(Perl/PCRE版本):
(?xsm) # free-spacing mode, multi-line
(?=.*?pig) # lookahead: if pig is not there, fail right away to save the effort
(?: # start counter-line-skipper (lines that don't include pig)
(?: # skip one line
^ #
(?:(?!pig)[^\r\n])* # zero or more chars not followed by pig
(?:\r?\n) # newline chars
)
# for each line skipped, let Group 1 match an ever increasing portion of the numbers string at the bottom
(?= # lookahead
[^:]+ # skip all chars that are not colons
( # start Group 1
(?(1)\1) # match Group 1 if set
:\d+ # match a colon and some digits
) # end Group 1
) # end lookahead
)*+ # end counter-line-skipper: zero or more times
.*? # match
\K # drop everything we've matched so far
pig # match pig (this is the match!)
(?=[^:]+(?(1)\1):(\d+)) # capture the next number to Group 2
替换:
\2
输出:
my cat
dog
my 3
my cow
my mouse
:1:2:3:4:5:6:7
pig
),将pig
的第一次出现移动到另一行,并查看它如何影响结果。:
相当常见,可能会发生在其他地方。我们可以发明一个UNIQUE_DELIMITER
并稍微调整表达式。但是以下优化更有效,让我们保持:
第二个解决方案的优化:数字字符串反转:7:6:5:4:3:2:1
在我们的预测中,这使我们能够通过简单的.*
到达输入的底部,并从那里开始回溯。由于我们知道我们在字符串的末尾,因此我们不必担心:digits
成为字符串的另一部分。以下是如何做到这一点。my cat pi g
dog p ig
my pig
my cow
my mouse
:7:6:5:4:3:2:1
搜索:
(?xsm) # free-spacing mode, multi-line
(?=.*?pig) # lookahead: if pig is not there, fail right away to save the effort
(?: # start counter-line-skipper (lines that don't include pig)
(?: # skip one line that doesn't have pig
^ #
(?:(?!pig)[^\r\n])* # zero or more chars not followed by pig
(?:\r?\n) # newline chars
)
# Group 1 matches increasing portion of the numbers string at the bottom
(?= # lookahead
.* # get to the end of the input
( # start Group 1
:\d+ # match a colon and some digits
(?(1)\1) # match Group 1 if set
) # end Group 1
) # end lookahead
)*+ # end counter-line-skipper: zero or more times
.*? # match
\K # drop match so far
pig # match pig (this is the match!)
(?=.*(\d+)(?(1)\1)) # capture the next number to Group 2
替换: \2
请查看演示中的替换。
此解决方案仅适用于.NET。
搜索:
(?m)(?<=\A(?<c>^(?:(?!pig)[^\r\n])*(?:\r?\n))*.*?)pig(?=[^:]+(?(c)(?<-c>:\d+)*):(\d+))
带注释的自由间隔版本:
(?xm) # free-spacing, multi-line
(?<= # lookbehind
\A #
(?<c> # skip one line that doesn't have pig
# The length of Group c Captures will serve as a counter
^ # beginning of line
(?:(?!pig)[^\r\n])* # zero or more chars not followed by pig
(?:\r?\n) # newline chars
) # end skipper
* # repeat skipper
.*? # we're on the pig line: lazily match chars before pig
) # end lookbehind
pig # match pig: this is the match
(?= # lookahead
[^:]+ # get to the digits
(?(c) # if Group c has been set
(?<-c>:\d+) # decrement c while we match a group of digits
* # repeat: this will only repeat as long as the length of Group c captures > 0
) # end if Group c has been set
:(\d+) # Match the next digit group, capture the digits
) # end lokahead
替换为: $1
(?m)\A((?:^.*?(?:(?=pig)|$)[\r\n]*(?=[^:]+(\2?+:(\d+))))+)pig
(所以,去掉懒惰)并替换为 \1\3
将更加“文本编辑器友好” :) 不过我认为 OP 已经不再关心了。 - Robin:%s/searched_word/\=printf('%-4d', line('.'))/g
(阅读更多)。但正如其他人提到的,这不是一个适合在SO上询问的问题,而是适合在Super User上询问的问题 ;)除非扩展一个允许任意扩展的编辑器,否则我不知道有哪个编辑器可以做到这一点。
不过,你可以很容易地使用 perl
来完成这个任务。
perl -i.bak -e"s/word/$./eg" file
或者如果你想使用通配符,
perl -MFile::DosGlob=glob -i.bak -e"BEGIN { @ARGV = map glob($_), @ARGV } s/word/$./eg" *.txt
File::DosGlob
有哪些特性是他们可能需要的?我从来没有需要超出*.ext
的东西,普通的glob
可以很好地处理它,但我怀疑我不知道dos glob的附加功能是什么。 - Millerglob
与Windows glob非常不同。它在常见表达式中会失败。 DosGlob glob
无法做到内置的那个不能做到的事情。它只是以不同的方式实现。 - ikegami~$ perl -pe 's/word/$./g;' file > tmp
有效。我猜这些答案在WSL的使用越来越普遍时变得更加重要:learn.microsoft.com/en-us/windows/wsl/about - undefined~$ raku -ne 'state $i; ++$i; put m/word/ ?? $i !! $_;' file
word
的每个实例:~$ raku -pe 'state $i; ++$i; s:g/word/{$i}/;' file
-ne
非自动打印的逐行标志,声明一个计数器变量$i
。使用++i
递增变量。使用Raku的三元运算符Test ??
True !!
False,如果找到与word
匹配的内容,则输出(即put
)递增的$i
变量,否则输出原始行$_
。-pe
自动打印每行标志,声明一个计数变量$i
。使用++i
递增变量。使用Raku的s:g///
全局替换操作符,将每个匹配到的word
替换为计数器$i
。my cat
dog
my pig
my cow
my mouse
my pig also
使用 pig
替换:
my cat
dog
my 3
my cow
my mouse
my 6 also
$++
来完成,这将以0为起始索引而不是1。正则表达式匹配器实际上可以写成/ … /
,即不使用m
,如果使用斜杠,甚至可以写成m{ … }
,如果你想在正则表达式中匹配斜杠。:g
全局之外,您还可以为m/ … /
或s///
匹配器添加一些正则表达式的"副词",其中最有用的可能是:i
用于不区分大小写的匹配,如下所示:
m:i/ … /;
或 s:i:g/…/…/;
有关正则表达式副词的更多信息,请参见底部。
~$ vim file
打开文件,然后进入命令行模式,使用:
冒号。一旦进入Vim命令行,输入%! raku -pe 'state $i; ++$i; s:g/pig/{$i}/;'
来运行Raku命令。根据需要,可以保存到新文件或覆盖原始文件。