如何使用Perl对具有多个位置的字符串进行子串?

时间:2018-04-17 07:20:23

标签: perl substring substr

我有几个地方想要在几个部分剪下我的弦 例如:

$string= "AACCAAGTAA";
@cut_places= {0,4, 8 };

我的$string应如下所示:AACC AAGT AA;
我怎么能这样做?

3 个答案:

答案 0 :(得分:7)

要填充数组,请使用圆括号,而不是大括号(它们用于散列引用)。

一种可能的方法是使用substr,其中第一个参数是位置,因此您可以使用数组元素。你只需要通过从下一个中减去位置来计算长度;并且为了能够计算最后的长度,你也需要整个字符串的长度:

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my $string = 'AACCAAGTAA';
my @cut_places = (0, 4, 8);

push @cut_places, length $string;
my @parts = map {
    substr $string, $cut_places[$_], $cut_places[$_+1] - $cut_places[$_]
} 0 .. $#cut_places - 1;

say for @parts;

如果原始数组包含长度而不是位置,则代码将更容易。

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

my $string = 'AACCAAGTAA';
my @lengths = (4, 4, 2);  # 4, 4, 4 would work, too

my @parts = unpack join("", map "A$_", @lengths), $string;

say for @parts;

有关详细信息,请参阅unpack

答案 1 :(得分:0)

首先计算出所需部件的长度,然后所有方法都更容易。这里使用了正则表达式

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

my $string = 'AACCAAGTAA';

my @pos = (0, 4, 8); 

my @lens = do {
    my $prev = shift @pos;
    "$prev", map { my $e = $_ - $prev; $prev = $_; $e } @pos;
};

my $patt = join '', map { '(.{'.$_.'})' } @lens;
my $re = qr/$patt/;

my @parts = grep { /./ } $string =~ /$re(.*)/g;
say for @parts; 

通过减去连续位置2-1,3-2(等)来计算长度@lens。我只使用do,以便其他地方不需要的@prev变量不会污染"其余的代码。 引用"$prev"以便在map更改之前首先对其进行评估。

正则表达式返回的匹配项通过grep传递,以过滤由0位置引起的空字符串(或每当连续位置相同时)。

这适用于任何长度的位置数组,只要位置与字符串一致即可。

答案 2 :(得分:0)

这是一个解决方案,首先计算位置列表中的前向差异。字符串的长度首先附加到列表的末尾,它还没有跨越完整的字符串

然后使用差异来构建unpack格式字符串,用于构建所需的子字符串序列。

我已将该功能编写为do块,如果需要,可以很容易地转换为子例程。

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

my $string     = 'AACCAAGTAA';
my @cut_places = ( 0, 4, 8 );

my @parts = do {

    my @places = @cut_places;
    my $len    = length $string;
    push @places, $len unless $places[-1] >= $len;

    my @w    = map { $places[$_]-$places[$_-1] } 1 .. $#places;
    my $patt = join ' ', map { "A$_" } @w;

    unpack $patt, $string;
};

say "@parts";

输出

AACC AAGT AA