匹配Perl中字符串中的字母循环

时间:2014-05-05 13:10:22

标签: regex string perl

我们说我有一个字符串' abc'。如何匹配所有3次或更多次出现的' abc'和它的周期(' bca' cab')在一个大字符串中。

现在我正在使用单个条目作为正则表达式匹配,但是a)由于字符串非常大而花费的时间太长,并且b)我在后续匹配中获得相同的区域。例如,如果我的输入是:

dabcabcabcabgyklagkbcabcabcahkgljla
 ^-------^         ^-------^

我希望我的输出是两个匹配:

1. abcabcabc    position 2
2. bcabcabca    position 20

现在我得到了4行输出:

1. abcabcabc    position 2
2. bcabcabca    position 3
3. cabcabcab    position 4
4. bcabcabca    position 20

我希望我解释了我的问题。我通过在单个正则表达式中使用所有可能的组合进行多重正则表达式匹配,以另一种复杂的方式获得所需的输出:

while($str =~ /(abc){3,}|(bca){3,}|cab{3,}/g {
    print "$1\tposition $-[0]\n";
}

但这是一个严重的性能损失,并且考虑到我的输入的大小,它需要永远运行。请帮我一个更有效的算法。如果之前有人问过,真的很抱歉,但我找不到任何帮助我的页面。

提前致谢

3 个答案:

答案 0 :(得分:0)

我建议你只使用/(abc){2,}/前面没有任何内容,cbc,后跟任何内容,aab,所以

/ ( (?:b?c)? (?:abc){2,} (?:ab?)? ) /xg

我们的想法是将任何序列(例如bcabcabcabcabca)分解为多个abc,可能先于bt c或(此处)bc,然后可能会跟随(此处)aab,就像这样。

bc abcabcabcabc a

这样正则表达式引擎不必在每个点检查三个不同的字符串。

这样做可能会发现最多比您需要的三个字符短的序列,但它应该更快,您可以在长度上添加额外的过滤器。喜欢这个

use strict;
use warnings;

my $seq = 'dabcabcabcabgyklagkbcabcabcahkgljla';

while ($seq =~ / ( (?:b?c)? (?:abc){2,} (?:ab?)? ) /xg) {
  next unless length $1 >= 9;
  my $subseq = $1;
  chop $subseq while length($subseq) % 3;
  print "$subseq\tposition $-[0]\n";
}

<强>输出

abcabcabc position 1
bcabcabca position 19

答案 1 :(得分:0)

我已经使用了您发布的数据并找到了原始解决方案的变体,其运行速度比原始解决方案快四到五倍。不幸的是,你发布的序列只有225KB而且其中只有一个SSR,所以我不知道它的代表性。

基本上,相反寻找模式的四个旋转序列,它只查找核心SSR的重复,带有可选的前缀和后缀,使整个序列从SSR内的任何地方开始,如下所示

/ (?:AAT|AT|T|) (?:AAAT){3,} (:?AAA|AA|A|) /x

所有这个正则表达式都是自动构建的。

use strict;
use warnings;
use autodie;

open my $fh, '<', 'chr1.txt';
my $seq = <$fh>;
close $fh;

my @ssrs = qw( AAAT AAAC AACC AACG );

retrieve_ssr('Sample', $seq, \@ssrs);

sub retrieve_ssr {
  my ($name, $seq, $ssr_list) = @_;

  for my $ssr (@$ssr_list) {

    my $len = length $ssr;
    my $n = $len == 5 ? 3 : 12 / $len;
    $n = 1;

    my $prefix = join '', map { substr($ssr, -$_) . '|' }   1 .. $len-1;
    my $suffix = join '', map { substr($ssr, 0, $_) . '|' } reverse 1 .. $len-1;

    my $re = qr/ (?:$prefix) (?:$ssr){$n,} (?:$suffix) /x;

    while ($seq =~ /$re/g) {

      my $start  = $-[0] + 1;
      my $length = $+[0] - $-[0];

      my $excess = $length % $len;
      pos($seq) -= $excess;
      $length -= $excess;

      my $seq = substr $seq, $-[0], $length;
      print "$start\t$+[0]\t$length\t$seq\n";
    }
  }
}

<强>输出

23738 23752 12  TAAATAAATAAA

答案 2 :(得分:-1)

让我觉得你不需要有3个独立的正则表达式,你真的只需要一个像这样的正则表达式:

perl -ne 'print "$1\tposition $-[0]\n" while /(b?c?(abc){1,}a?b?)/g' mydata.txt

这个想法是根据需要匹配核心模式abc,然后你只需要考虑“b?c?”的潜在前缀。和潜在的后缀“a?b?” (如果前缀或后缀较长,那么它将与中心的主正则表达式匹配。)

如果给定此表达式将找到3个字符或更长的匹配,但显然可以通过更改{1,}

中的值来提高最小长度

此解决方案确实会冒一些前缀和后缀误报的风险,因为它会匹配“babc”,因此您可以对结果进行第二次慢速搜索以获得完全准确性。