捕获模式的多个实例

capturing multiple instances of a pattern

我有一个字符串:

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

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

我试过了:

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

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

您可以先使用

验证字符串
/\A ({ [^{}]* }) (?: [\/+*-] (?1))* \z/x

详情:

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

然后,您可以通过

收集个人比赛
/ { [^{}]* } | [\/+*-] /gx

此正则表达式匹配 {}(带 {[^{}]*})或 /+、[=23= 之间的所有子字符串] 或 - 个字符([\/+*-])。

看到一个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}

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

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

模式匹配

  • \G 断言位置在上一个匹配项的末尾,或在字符串的开头(在本例中)
  • (?=.*{[^{}]*}\z) 正面前瞻,断言字符串以卷曲部分结尾
  • ({[^{}]*}) 捕获组 1 中的花括号
  • ([+*\/-])? 可选择捕获第 2 组中的运算符

Regex demo | Perl demo

例子

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

输出

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

分词器方法:

my @tokens;
for ($str) {
   while (1) {
      /\G \s+ /xgc;

      /\G \{ ( [^{}]* ) \} /xgc
         and do { push @tokens, [ VALUE =>  ]; next; };

      /\G ( [+-*\/] ) /xgc
         and do { push @tokens, [ OP =>  ]; next; };

      /\G \Z /xgc
         and last;

      die( "Unexpected character at pos ".( pos )."\n" );
   }
}

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

如果您只有非嵌套块,由已知的运算符列表分隔,则可以使用 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}
'
        ];

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