Regex 如何在没有灾难性回溯的情况下编写这个正则表达式

Regex 如何在没有灾难性回溯的情况下编写这个正则表达式,regex,perl,pcre,Regex,Perl,Pcre,我正在尝试编写一个正则表达式,它将为以I开头的行获取列表中第21个字段的内容,前提是该字段包含格式为nnn-nnnnnn(如001-123456)的数字: 这是我正在使用的简单正则表达式,我正在第二个捕获组中捕获字段内容: ^I(\|.*?){20}(\d{3}-\d{6}) 我读过关于灾难性回溯的文章,但是我的正则表达式技能有限,我不知道如何编写这个正则表达式,这样我就不会得到灾难性回溯 非常感谢您的帮助。您可以使用否定模式避免灾难性的回溯: ^I(?:\|[^|]*){20}(\d{3}-

我正在尝试编写一个正则表达式,它将为以I开头的行获取列表中第21个字段的内容,前提是该字段包含格式为nnn-nnnnnn(如001-123456)的数字:

这是我正在使用的简单正则表达式,我正在第二个捕获组中捕获字段内容:

^I(\|.*?){20}(\d{3}-\d{6})
我读过关于灾难性回溯的文章,但是我的正则表达式技能有限,我不知道如何编写这个正则表达式,这样我就不会得到灾难性回溯

非常感谢您的帮助。

您可以使用否定模式避免灾难性的回溯:

^I(?:\|[^|]*){20}(\d{3}-\d{6})
[^ |]*
匹配0个或多个非


在我看来,更好的方法是在管道上拆分字符串,然后检查第一个和第21个字段。命令行中带有autosplit参数
-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 $1 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  
使用严格;
使用警告;
使用特征qw(例如);
我的@F;
while(){
@F=拆分/\ |/;
如果$F[0]等式'I'和&$F[20]=~/(\d+-\d+),则说$1/
}
__资料__
T | 112 | | | | | AZ | D | 1 | 1|
每根10.00-.99 | 36 | 1 | 1 | D | I | CO | BTE | N | 00 | 15 | 1 | 001-123456 | ACAA
我20度酒色盎司2.00-6.50-4.48-1度酒色盎司
我20度酒色盎司2.00-6.50度4.48度1度酒色盎司45度1度
我20度酒色盎司2.00-6.50-4.48-1度酒色盎司

当你知道它的格式是
nnn-nnnn
为什么不直接使用
\d{3}-\d{6}
?@noob我正在使用它,但我只对第21个字段感兴趣-我不想在任何其他字段中匹配此字符串。因为您其他字段没有该模式的编号,仅使用该模式将匹配第21个字段。@noob文章中的数据只是一个示例。您使用了错误的方法。您需要在管道上拆分字符串,然后检查第一个字段是否为
I
,第21个字段是否为空(或者是否具有所需的格式)。这是解决问题的正确方法。正则表达式解析一个带分隔符的文件是非常痛苦的。
use strict;
use warnings;
use feature qw(say);

my @F;
while(<DATA>) {
    @F = split /\|/;
    say $1 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