Perl 与正则表达式匹配一个数字和与数字在字符串中指定的一样多的后续字符

Perl match with regex a number and as many following characters as the number specifies within a string

我(认为我)在 Perl 方面很有经验,但我仍然有一个棘手的问题要解决。 我必须以这种格式匹配一个字符串(其格式我无法从生物信息学软件中更改出来):

[\+\-][0-9]+[ACGTacgt]+

实际上这很容易,尽管模式 [ACGTacgt] 的重复次数不完全是 1 或更多,而是 [0-9]+ 定义的次数 所以它可以是

[...whatever...]+2ac[...whatever...]
+4acta
+3atg

等..

现在要测试正则表达式是否有效,我只是在玩一个替换,我尝试了以下方法:

$mystring =~ s/[\+\-]([0-9]+)[ACGTacgt]{}//g

不幸的是,上面的这个人不起作用,我收到一个错误,抱怨未转义的大括号。事实上,如果我定义一个合适的数字而不是 \1,事情就会起作用:

$mystring =~ s/[\+\-]([0-9]+)[ACGTacgt]{1}//g

我需要它工作,因为格式可能包含像 ac.,.+2caaa..a.c 这样的序列,我必须从中准确地得到 +2ca 与其余部分分开。

是否可以一步到位,或者我现在缺少一个合乎逻辑的原因是不可能的?

感谢您的帮助或建议!

贝鲁蒂

正则表达式的{$N}部分是一个修饰符,它不能使用反向引用作为它的计数。您可以使用嵌入式 perl 表达式解决它:

use strict;
use warnings;
my $string = 'ac.,.+2caaa..a.c';
$string =~ s/[+-]([0-9]+)(??{ "[ACGTacgt]{}" })//g;
print "$string\n";

请注意,embedded subexpressions 是最后的手段,并且出于明显的原因阻止了正则表达式的正确优化 - 对于必须删除匹配的子字符串的这种确切情况,IMO 是一个适当的权衡,但如果你要求略有不同,拆分迭代方法可能更合适。

可以迭代数字并在循环体中匹配捕获的字母数量

use warnings;
use strict;
use feature 'say';

my $s = q(ac.,.+2caaa..a.c-3acgg+1tt);

while ($s =~ /[+-]([0-9]+)/g) { 
    my $c = ; 
    $s =~ /\G([acgt]{$c})/i or next;

    say "$c";  # or process it further / store it ...
}

\G 断言使其正则表达式根据需要从前一个 m//g 匹配结束的地方开始。这是“链全局匹配”的标准方法,通常通过协调多个正则表达式来扫描文本。请参阅 Assertions in perlre and, for far more detail, in perlop 中的文档(搜索 \G)。

版画

2ca
3acg
1t

如果还需要提取 [+-],请在其周围添加捕获括号并重新枚举捕获(即 </code> 和 <code> 中的数字)

请阐明其他要求 -- 例如:您是否只需要提取模式,或者原始字符串也应该发生什么特别的事情吗?


Update 明确指出匹配项也需要从字符串中删除。

一个简单的方法是在收集它们之后用另一个正则表达式简单地删除它们。

经过与上述相同的处理后,收集到的匹配项用于形成一个交替的模式,以便将其删除。这也是有效的,因为通过构造,交替中的子模式按照它们在字符串中出现的顺序出现

use warnings;
use strict;
use feature 'say';

my $string = q(ac.,.+2caaa..a.c-3acgg+1tt);

my @matches;

while ($string =~ /([+-])([0-9]+)/g) { 
    my ($sign, $count)  = (, );
    $string =~ /\G([acgt]{$count})/i or next;    
    push @matches, $sign.$count.; 
}    
say for @matches;

my $matches_re = '(?:' . join('|', map { quotemeta } @matches) . ')';

$string =~ s/$matches_re//g;    
say $string;

我现在将标志 [+-] 加入比赛。

它打印

+2ca
-3acg
+1t
ac.,.aa..a.cgt