【问题标题】:capturing multiple instances of a pattern捕获模式的多个实例
【发布时间】:2022-01-20 09:27:20
【问题描述】:

我有一个字符串:

{value1}+{value2}-{value3}*{value...n}

使用正则表达式,我想捕获每个括号内的值以及它们之间的运算符,我不知道会有多少个括号。

我试过了:

/(\{.*\}).*([\+|\-|\*|\/])*/mgU

但这只是让我得到值而不是运算符。我哪里做错了?

【问题讨论】:

  • 尝试匹配\{[^{}]*}|[+\/*-]
  • 尝试使用 2 个捕获组,其中第二个在非捕获组中 {([^{}]*)}(?:([-+*])|$) regex101.com/r/9wi6Z0/1
  • 您能否确认您的字符串格式是否始终相同?
  • 字符串将至少有一部分匹配 {value},但也可以有多个 {value} 匹配,由运算符 +、-、* 或 / 分隔。我的目标是识别与此模式匹配的字符串,然后将这些部分分开以供稍后在 perl 脚本中使用。

标签: regex perl


【解决方案1】:

您可以先验证字符串

/\A ({ [^{}]* }) (?: [\/+*-] (?1))* \z/x

详情

  • \A - 字符串开头
  • ({[^{}]*}) - 第 1 组:{,除 {} 之外的任何零个或多个字符,然后是 } 字符
  • (?:[\/+*-](?1))* - /+*- char 出现零次或多次,然后是 Group 1 模式
  • \z - 字符串结束。

然后,您可以收集个人匹配项

/ { [^{}]* } | [\/+*-] /gx

此正则表达式匹配所有出现在{} 之间的任何子字符串(带有{[^{}]*})或/+*- 字符(带有[\/+*-])。

查看complete demo script

#!/usr/bin/perl
use strict;
use warnings;
 
my $text = "{value1}+{value2}-{value3}*{value...n}";
 
if ($text =~ /\A ({ [^{}]* }) (?: [\/+*-] (?1))* \z/x) {
    while($text =~ / { [^{}]* } | [\/+*-] /gx) {
        print "$&\n";
    }
}

输出:

{value1}
+
{value2}
-
{value3}
*
{value...n}

【讨论】:

    【解决方案2】:

    另一个想法可能是使用 \G 锚和 2 个捕获组,其中卷曲值在第 1 组中,运算符在第 2 组中:

    \G(?=.*{[^{}]*}\z)({[^{}]*})([+*\/-])?
    

    模式匹配

    • \G 断言位置在上一个匹配的末尾,或者在字符串的开头(在这种情况下)
    • (?=.*{[^{}]*}\z) 正向前瞻,断言字符串以卷曲部分结尾
    • ({[^{}]*}) 捕获第一组花括号
    • ([+*\/-])? 可选择捕获组 2 中的操作员

    Regex demo | Perl demo

    例子

    my $str = "{value1}+{value2}-{value3}*{value...n}";
    while ($str =~ /\G(?=.*\{[^{}]*}\z)({[^{}]*})([+*\/-])?/g) {
        print "Curly value: $1 Operator: $2\n";
    }
    

    输出

    Curly value: {value1} Operator: +
    Curly value: {value2} Operator: -
    Curly value: {value3} Operator: *
    Curly value: {value...n} Operator:
    

    【讨论】:

      【解决方案3】:

      分词器方法:

      my @tokens;
      for ($str) {
         while (1) {
            /\G \s+ /xgc;
      
            /\G \{ ( [^{}]* ) \} /xgc
               and do { push @tokens, [ VALUE => $1 ]; next; };
      
            /\G ( [+-*\/] ) /xgc
               and do { push @tokens, [ OP => $1 ]; next; };
      
            /\G \Z /xgc
               and last;
      
            die( "Unexpected character at pos ".( pos )."\n" );
         }
      }
      

      这可能有点矫枉过正,但它更容易扩展。

      【讨论】:

      • 在不知道大括号内可能有什么的情况下,这是一个好的开始。否则,Dijkstra 的两层计算器可能会起作用。
      【解决方案4】:

      如果您只有非嵌套块,由已知的运算符列表分隔,您可以使用split 轻松地将语句分隔为值和运算符。

      use strict;
      use warnings;
      use Data::Dumper;
      
      my @val = split m#([-+/*])#, <DATA>;   # parens will prevent operators from being consumed
      print Dumper \@val;
      
      __DATA__
      {value1}+{value2}-{value3}*{valuen}/{value4}+{value5}-{value6}*{valuen}+{value7}+{value8}-{value9}
      

      这将打印:

      $VAR1 = [
                '{value1}',
                '+',
                '{value2}',
                '-',
                '{value3}',
                '*',
                '{valuen}',
                '/',
                '{value4}',
                '+',
                '{value5}',
                '-',
                '{value6}',
                '*',
                '{valuen}',
                '+',
                '{value7}',
                '+',
                '{value8}',
                '-',
                '{value9}
      '
              ];
      

      从那里,验证和清理值以及识别运算符应该是一项简单的任务。

      【讨论】:

      • 我开始回答这样的问题是因为我喜欢 split 的分隔符保留模式。只要这些值不包含运算符字符,它就很聪明。
      • @briandfoy 这是一个非常简单的解决方案,很吸引人。
      猜你喜欢
      • 1970-01-01
      • 2012-08-08
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-27
      相关资源
      最近更新 更多