繁体   English   中英

如何grep/perl/awk重叠正则表达式

[英]How to grep/perl/awk overlapping regex

试图通过管道将字符串导入 grep/perl 正则表达式以提取重叠的匹配项。 目前,结果似乎只提取了没有任何“回顾”的连续匹配:

尝试使用 egrep(在 GNU 和 BSD 上):

$ echo "bob mary mike bill kim jim john" | egrep -io "[a-z]+ [a-z]+"
bob mary
mike bill
kim jim

尝试使用 perl 风格的 grep (-P):

$ echo "bob mary mike bill kim jim john" | grep -oP "()[a-z]+ [a-z]+"
bob mary
mike bill
kim jim

尝试使用 awk 只显示第一个匹配项:

$ echo "bob mary mike bill kim jim john" | awk 'match($0, /[a-z]+ [a-z]+/) {print substr($0, RSTART, RLENGTH)}'
bob mary

我想从一个简单的工作 bash 管道命令中看到的重叠结果是:

bob mary
mary mike
mike bill
bill kim
kim jim
jim john

有任何想法吗?

Lookahead 是你的朋友

echo "bob mary mike bill kim jim john" | 
    perl -wnE'say "$1 $2" while /(\w+)\s+(?=(\w+))/g'

关键是前瞻,作为“零宽度断言”,不消耗任何东西——同时它仍然允许我们捕获其中的模式。

因此,当正则表达式引擎匹配一个单词和空格( (\\w+)\\s+ ),吞噬它们时,它然后停在那里并“向前看”,只是为了“断言”所寻求的模式在那里; 正如他们所说,它不会从最后一个空格和下一个\\w之间的位置移动,也不会“消耗”下一个单词。

很高兴我们也可以捕捉到“可见”的模式,即使它没有被消耗掉! 所以我们得到了$1$2 ,两个词。

然后,由于/g修饰符,引擎继续寻找另一个单词+空格,然后是另一个单词。 下一个词是我们的前瞻发现的词——所以现在一个词被消耗了,但下一个词“寻找”(并捕获)。 等等。

请参阅perlretut 中的 Lookahead 和 Lookbehind 断言

你也可以使用awk

awk '{for(i=1;i<NF;i++) print $i,$(i+1)}' <<< 'bob mary mike bill kim jim john'

请参阅在线演示 此解决方案迭代所有以空格分隔的字段并打印当前字段 ( $i ) + 字段分隔符(此处为空格)+ 后续字段值( $(i+1) )。

或者,另一种perl解决方案使用一种非常常见的技术来捕获正向前瞻中的重叠模式:

perl -lane 'while (/(?=\b(\p{L}+\s+\p{L}+))/g) {print $1}' <<< 'bob mary mike bill kim jim john'

请参阅在线演示 详情

  • (?= - 正向前瞻的开始
    • \\b - 单词边界
    • (\\p{L}+\\s+\\p{L}+) - 捕获组 1:一个或多个字母、一个或多个空格、一个或多个字母
  • ) - 前瞻结束。

此处,仅打印第 1 组值 ( {print $1} )。

性能考虑

至于这里的 Perl 解决方案,我的结果是最慢的,而 Timur 是最快的,但是, awk解决方案结果证明比任何 Perl 解决方案都快。 结果:

# ./wiktor_awk.sh

real    0m17.069s
user    0m12.264s
sys     0m5.314s

# ./timur_perl.sh

real    0m18.201s
user    0m15.612s
sys     0m6.139s

# ./zdim.sh

real    0m23.559s
user    0m19.883s
sys     0m7.359s

# ./wiktor_perl.sh

real    2m12.528s
user    1m52.857s
sys     0m20.201s

注意我为每个解决方案创建了 *.sh 文件,例如

#!/bin/bash
N=10000
time(
 for i in $(seq 1 $N); do
   <SOLUTION_HERE> &>/dev/null;
done)

for f in *.sh; do chmod +x "$f"; done运行for f in *.sh; do chmod +x "$f"; done for f in *.sh; do chmod +x "$f"; done for f in *.sh; do chmod +x "$f"; done (从这里借来的)。

使用下面的 Perl one-liners,它避免了前瞻(它仍然可以是你的朋友):
对于以空格分隔的单词:

echo "bob mary mike bill kim jim john" | perl -lane 'print "$F[$_] $F[$_+1]" for 0..($#F-1);'

对于在 Perl 中定义为\\w+的单词,由非单词字符\\W+分隔:

echo "bob.mary,mike'bill kim jim john" | perl -F'/\W+/' -lane 'print "$F[$_] $F[$_+1]" for 0..($#F-1);'

Perl 单行使用这些命令行标志:
-e :告诉 Perl 查找内嵌代码,而不是在文件中。
-n :一次循环输入一行,默认情况下将其分配给$_
-l :在执行内联代码之前去除输入行分隔符(默认情况下 *NIX 上的"\\n" ),并在打印时附加它。
-a :在空格或-F选项中指定的正则表达式上将$_拆分为数组@F
-F'/\\W+/' :在\\W+ (一个或多个非单词字符)上拆分为@F ,而不是在空格上。

$#F :数组@F的最后一个索引,输入行被拆分为该索引。
0..($#F-1) :索引(数字)的范围,从数组@F的第一个 ( 0 ) 到倒数第二个 ( $#F-1 ) 索引。
$F[$_]$F[$_+1] :数组@F两个连续元素,分别具有索引$_$_+1

也可以看看:
perldoc perlrun : 如何执行 Perl 解释器:命令行开关
perldoc perlre : Perl 正则表达式 (regexes)
perldoc perlre : Perl 正则表达式(regexes):量词; 字符类和其他特殊转义; 断言; 捕获组
perldoc perlrequick : Perl 正则表达式快速perldoc perlrequick

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM