如何在没有灾难性回溯的情况下编写这个正则表达式
How to write this regex without catastrophic backtracking
我正在尝试编写一个正则表达式,它将为我获取此列表中以 I 开头的行的第 21 个字段的内容,前提是该字段包含此格式的数字 nnn-nnnnnn(如 001 -123456):
T|112|| | | |AZ |D |1 | 1|
I| 10|ACAA |BY CORD EACH | 10.00-| .99 | | .36 |1 | 1|D |I|CO |BTE |N| | .00 | .00 |15 |1 |001-123456 |ACAA
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234555 |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 | |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234552 |JEE
这是我正在使用的简单正则表达式,我在第二个捕获组中捕获字段内容:
^I(\|.*?){20}(\d{3}-\d{6})
我读过灾难性回溯,但我的正则表达式技能有限,我不明白如何编写这个正则表达式,这样我就不会得到灾难性回溯。
不胜感激。
您可以通过使用否定模式来避免灾难性回溯:
^I(?:\|[^|]*){20}(\d{3}-\d{6})
[^|]*
匹配 0 个或多个不是 |
的字符
IMO,更好的方法是在管道上拆分字符串,然后检查第一个和第 21 个字段。带有自动拆分参数 -a
:
的命令行示例
perl -F'\|' -anE'say $& if $F[0] eq "I" && $F[20]=~/\S+/' file
脚本中的示例:
use strict;
use warnings;
use feature qw(say);
my @F;
while(<DATA>) {
@F = split /\|/;
say if $F[0] eq 'I' && $F[20] =~ /(\d+-\d+)/
}
__DATA__
T|112|| | | |AZ |D |1 | 1|
I| 10|ACAA |BY CORD EACH | 10.00-| .99 | | .36 |1 | 1|D |I|CO |BTE |N| | .00 | .00 |15 |1 |001-123456 |ACAA
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234555 |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 | |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234552 |JEE
我正在尝试编写一个正则表达式,它将为我获取此列表中以 I 开头的行的第 21 个字段的内容,前提是该字段包含此格式的数字 nnn-nnnnnn(如 001 -123456):
T|112|| | | |AZ |D |1 | 1|
I| 10|ACAA |BY CORD EACH | 10.00-| .99 | | .36 |1 | 1|D |I|CO |BTE |N| | .00 | .00 |15 |1 |001-123456 |ACAA
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234555 |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 | |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234552 |JEE
这是我正在使用的简单正则表达式,我在第二个捕获组中捕获字段内容:
^I(\|.*?){20}(\d{3}-\d{6})
我读过灾难性回溯,但我的正则表达式技能有限,我不明白如何编写这个正则表达式,这样我就不会得到灾难性回溯。
不胜感激。
您可以通过使用否定模式来避免灾难性回溯:
^I(?:\|[^|]*){20}(\d{3}-\d{6})
[^|]*
匹配 0 个或多个不是 |
IMO,更好的方法是在管道上拆分字符串,然后检查第一个和第 21 个字段。带有自动拆分参数 -a
:
perl -F'\|' -anE'say $& if $F[0] eq "I" && $F[20]=~/\S+/' file
脚本中的示例:
use strict;
use warnings;
use feature qw(say);
my @F;
while(<DATA>) {
@F = split /\|/;
say if $F[0] eq 'I' && $F[20] =~ /(\d+-\d+)/
}
__DATA__
T|112|| | | |AZ |D |1 | 1|
I| 10|ACAA |BY CORD EACH | 10.00-| .99 | | .36 |1 | 1|D |I|CO |BTE |N| | .00 | .00 |15 |1 |001-123456 |ACAA
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234555 |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 | |JEE
I| 20|LEES03 |TINTED OZ | 2.00-| 6.50 | | 4.48 |1 | 1|D |I|FL |LTGE |N| | .00 | .00 |45 |1 |001-234552 |JEE