【问题标题】:Sliding window pattern match in perl or matlab regular expressionsperl 或 matlab 正则表达式中的滑动窗口模式匹配
【发布时间】:2016-05-17 15:34:57
【问题描述】:

我正在尝试使用 Perl 或 MATLAB 从单行文本中解析几个数字。我的文本行是:

t10_t20_t30_t40_

现在在matlab中,我使用了以下脚本

str = 't10_t20_t30_t40_';
a = regexp(str,'t(\d+)_t(\d+)','match')

然后它返回

a = 

't10_t20'    't30_t40'

我想要的是它也返回“t20_t30”,因为这显然是匹配的。为什么正则表达式不扫描它?

因此我转向 Perl,并在 Perl 中编写了以下内容:

#!/usr/bin/perl -w
$str = "t10_t20_t30_t40_";
while($str =~ /(t\d+_t\d+)/g)
{
    print "$1\n";
}

结果和matlab一样

t10_t20
t30_t40

但我真的希望“t20_t30”也出现在结果中。

谁能告诉我如何做到这一点?谢谢!

[更新解决方案]: 在同事的帮助下,我使用 Perl 提供的所谓“环视断言”确定了一个解决方案。

#!/usr/bin/perl -w
$str = "t10_t20_t30_t40_";
while($str =~ m/(?=(t\d+_t\d+))/g)
{print "$1\n";}

关键是在 Perl 中使用“零宽度前瞻断言”。当 Perl(和其他类似的包)使用正则表达式扫描字符串时,它不会重新扫描上次匹配中已扫描的内容。所以在上面的例子中,t20_t30 永远不会出现在结果中。为了捕捉到这一点,我们需要使用零宽度的前瞻搜索来扫描字符串,生成不排除任何子字符串从后续搜索中的匹配(参见上面的工作代码)。如果“全局”修饰符附加到搜索(即 m//g),则搜索将从第零位置开始并尽可能多地增加一,使其成为“贪婪”搜索。

这在this blog post中有更详细的解释。

表达式 (?=t\d+_t\d+) 匹配任何 0 宽度字符串,后跟 t\d+_t\d+,这将创建实际的“滑动窗口”。这有效地返回 $str 中的所有 t\d+_t\d+ 模式而没有任何排除,因为 $str 中的每个位置都是一个 0 宽度的字符串。附加括号在进行滑动匹配时捕获模式 (?=(t\d+_t\d+)) 并因此返回所需的滑动窗口结果。

【问题讨论】:

    标签: regex matlab perl sliding-window


    【解决方案1】:

    一旦regexp 算法找到了匹配,匹配的字符就不会被考虑用于进一步的匹配(通常,这是人们想要的,例如.* 应该匹配每个这篇文章的可想象的连续子串)。一种解决方法是在第一次匹配后重新开始搜索一个字符,并收集结果:

    str = 't10_t20_t30_t40_';
    sub_str = str;
    reg_ex = 't(\d+)_t(\d+)';
    start_idx = 0;
    all_start_indeces = [];
    all_end_indeces = [];
    off_set = 0;
    %// While there are matches later in the string and the first match of the
    %// remaining string is not the last character
    while ~isempty(start_idx) && (start_idx < numel(str))
        %// Calculate offset to original string
        off_set = off_set + start_idx;
        %// extract string starting at first character after first match
        sub_str = sub_str((start_idx + 1):end);
        %// find further matches
        [start_idx, end_idx] = regexp(sub_str, reg_ex, 'once');
        %// save match if any
        if ~isempty(start_idx)
            all_start_indeces = [all_start_indeces, start_idx + off_set];
            all_end_indeces = [all_end_indeces, end_idx + off_set];
        end
    end
    display(all_start_indeces)
    display(all_end_indeces)
    matched_strings = arrayfun(@(st, en) str(st:en), all_start_indeces, all_end_indeces, 'uniformoutput', 0)
    

    【讨论】:

    • 这是一个很好的解决方案,但我在网站上的声誉太低,我无法投票......对不起
    【解决方案2】:

    使用 Perl:

    #!/usr/bin/perl
    use Data::Dumper;
    use Modern::Perl;
    
    my $re = qr/(?=(t\d+_t\d+))/;
    
    my @l = 't10_t20_t30_t40' =~  /$re/g;
    say Dumper(\@l);
    

    输出:

    $VAR1 = [
              't10_t20',
              't20_t30',
              't30_t40'
            ];
    

    【讨论】:

      猜你喜欢
      • 2011-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多