Perl中的Regex Group:如何从正则表达式组中将元素捕获到数组中,以匹配字符串中未知数量的/多个/变量出现?

the*_*now 47 regex perl grouping match

在Perl中,如何使用一个正则表达式分组来捕获多个匹配它的事件到多个数组元素?

例如,对于字符串:

var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello
Run Code Online (Sandbox Code Playgroud)

用代码处理这个:

$string = "var1=100 var2=90 var5=hello var3=\"a, b, c\" var7=test var3=hello";

my @array = $string =~ <regular expression here>

for ( my $i = 0; $i < scalar( @array ); $i++ )
{
  print $i.": ".$array[$i]."\n";
}
Run Code Online (Sandbox Code Playgroud)

我想看看输出:

0: var1=100
1: var2=90
2: var5=hello
3: var3="a, b, c"
4: var7=test
5: var3=hello
Run Code Online (Sandbox Code Playgroud)

我会用什么作为正则表达式?

我想在这里匹配的东西之间的共性是一个赋值字符串模式,所以类似于:

my @array = $string =~ m/(\w+=[\w\"\,\s]+)*/;
Run Code Online (Sandbox Code Playgroud)

其中*表示与该组匹配的一个或多个事件.

(我使用split()打折,因为有些匹配本身包含空格(即var3 ...),因此不会产生预期的结果.)

有了上面的正则表达式,我只得到:

0: var1=100 var2
Run Code Online (Sandbox Code Playgroud)

正则表达式是否可能?还是需要添加代码?

在查找"perl regex multiple group"但没有足够的线索时,已经查看了现有的答案:

jkr*_*mer 43

my $string = "var1=100 var2=90 var5=hello var3=\"a, b, c\" var7=test var3=hello";

while($string =~ /(?:^|\s+)(\S+)\s*=\s*("[^"]*"|\S*)/g) {
        print "<$1> => <$2>\n";
}
Run Code Online (Sandbox Code Playgroud)

打印:

<var1> => <100>
<var2> => <90>
<var5> => <hello>
<var3> => <"a, b, c">
<var7> => <test>
<var3> => <hello>
Run Code Online (Sandbox Code Playgroud)

说明:

最后一块:最后的g标志意味着你可以多次将正则表达式应用于字符串.第二次它将继续匹配最后一个匹配在字符串中结束的位置.

现在为正则表达式:(?:^|\s+)匹配字符串的开头或一组一个或多个空格.这是必需的,所以当下次应用正则表达式时,我们将跳过键/值对之间的空格.这?:意味着括号内容不会被捕获为组(我们不需要空格,只需要键和值).\S+匹配变量名称.然后我们跳过任意数量的空格和两者之间的等号.最后,("[^"]*"|\S*)/匹配两个引号,其中包含任意数量的字符,或者该值的任意数量的非空格字符.请注意,引用匹配非常脆弱,并且不会正确处理escpaped引号,例如"\"quoted\""会导致"\".

编辑:

既然你真的想得到整个作业,而不是单个键/值,这里有一个单行提取:

my @list = $string =~ /(?:^|\s+)((?:\S+)\s*=\s*(?:"[^"]*"|\S*))/g;
Run Code Online (Sandbox Code Playgroud)


Gre*_*con 8

使用正则表达式,使用我喜欢称为弹力和伸展的技术:锚定在你知道将要存在的特征(大头钉)上,然后抓住(拉伸)之间的内容.

在这种情况下,您知道单个分配匹配

\b\w+=.+
Run Code Online (Sandbox Code Playgroud)

你有很多这样的重复$string.请记住,这\b意味着词边界:

单词boundary(\b)是两个字符之间的一个点,它有一个\w在它的一边和一个\W在它的另一边(按任意顺序),计算字符串的开头和结尾的虚构字符作为匹配a \W.

使用正则表达式描述赋值中的值可能有点棘手,但您也知道每个值都将以空格终止 - 尽管不一定是遇到的第一个空格! - 跟随另一个赋值或字符串结尾.

为了避免重复断言模式,请将其编译一次qr//并在模式中重复使用它以及前瞻性断言(?=...)以将匹配拉伸到足以捕获整个值的同时防止其溢出到下一个变量名中.

与列表上下文中的模式匹配m//g会产生以下行为:

/g修饰符指定全球模式匹配,也就是说,该字符串中的匹配多次越好.它的行为取决于上下文.在列表上下文中,它返回正则表达式中任何捕获括号匹配的子字符串列表.如果没有括号,则返回所有匹配字符串的列表,就好像整个模式周围有圆括号一样.

一旦前瞻看到另一个任务或行尾,该模式就会$assignment使用非贪婪.+?来切断值.请记住,匹配返回所有捕获子模式的子字符串,因此前瞻的交替使用非捕获(?:...).该qr//相反,含有隐含捕获括号.

#! /usr/bin/perl

use warnings;
use strict;

my $string = <<'EOF';
var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello
EOF

my $assignment = qr/\b\w+ = .+?/x;
my @array = $string =~ /$assignment (?= \s+ (?: $ | $assignment))/gx;

for ( my $i = 0; $i < scalar( @array ); $i++ )
{
  print $i.": ".$array[$i]."\n";
}
Run Code Online (Sandbox Code Playgroud)

输出:

0: var1=100
1: var2=90
2: var5=hello
3: var3="a, b, c"
4: var7=test
5: var3=hello


Eva*_*oll 7

我不是说这是你应该做的,但你要做的就是写一个语法.现在,你的例子是非常的语法简单,但达米安康威的模块正则表达式语法::真正伟大的这个.如果你必须发展这一点,你会发现它会让你的生活更轻松.我在这里使用了很多 - 它有点像perl6-ish.

use Regexp::Grammars;
use Data::Dumper;
use strict;
use warnings;

my $parser = qr{
    <[pair]>+
    <rule: pair>     <key>=(?:"<list>"|<value=literal>)
    <token: key>     var\d+
    <rule: list>     <[MATCH=literal]> ** (,)
    <token: literal> \S+

}xms;

q[var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello] =~ $parser;
die Dumper {%/};
Run Code Online (Sandbox Code Playgroud)

输出:

$VAR1 = {
          '' => 'var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello',
          'pair' => [
                      {
                        '' => 'var1=100',
                        'value' => '100',
                        'key' => 'var1'
                      },
                      {
                        '' => 'var2=90',
                        'value' => '90',
                        'key' => 'var2'
                      },
                      {
                        '' => 'var5=hello',
                        'value' => 'hello',
                        'key' => 'var5'
                      },
                      {
                        '' => 'var3="a, b, c"',
                        'key' => 'var3',
                        'list' => [
                                    'a',
                                    'b',
                                    'c'
                                  ]
                      },
                      {
                        '' => 'var7=test',
                        'value' => 'test',
                        'key' => 'var7'
                      },
                      {
                        '' => 'var3=hello',
                        'value' => 'hello',
                        'key' => 'var3'
                      }
                    ]
Run Code Online (Sandbox Code Playgroud)

  • +1因为我喜欢语法概念的想法(已经在计算机科学中研究过它们),尽管我还没有尝试过这个答案.我喜欢语法概念,因为这种方法可以应用于解决更复杂的问题,特别是在从传统的过时语言中解析代码/数据,迁移到新语言或数据驱动的系统/数据库时 - 这实际上是我的原因原始问题(虽然我当时没有提到它.) (2认同)

nic*_*men 5

可能有点过头了,但这是我查看http://p3rl.org/Parse::RecDescent的借口。做一个解析器怎么样?

#!/usr/bin/perl

use strict;
use warnings;

use Parse::RecDescent;

use Regexp::Common;

my $grammar = <<'_EOGRAMMAR_'
INTEGER: /[-+]?\d+/
STRING: /\S+/
QSTRING: /$Regexp::Common::RE{quoted}/

VARIABLE: /var\d+/
VALUE: ( QSTRING | STRING | INTEGER )

assignment: VARIABLE "=" VALUE /[\s]*/ { print "$item{VARIABLE} => $item{VALUE}\n"; }

startrule: assignment(s)
_EOGRAMMAR_
;

$Parse::RecDescent::skip = '';
my $parser = Parse::RecDescent->new($grammar);

my $code = q{var1=100 var2=90 var5=hello var3="a, b, c" var7=test var8=" haha \" heh " var3=hello};
$parser->startrule($code);
Run Code Online (Sandbox Code Playgroud)

产量:

var1 => 100
var2 => 90
var5 => hello
var3 => "a, b, c"
var7 => test
var8 => " haha \" heh "
var3 => hello
Run Code Online (Sandbox Code Playgroud)

附注。请注意双 var3,如果您希望后一个分配覆盖第一个分配,您可以使用散列来存储值,然后再使用它们。

聚苯乙烯。我的第一个想法是在 '=' 上拆分,但是如果字符串包含 '=' 则会失败,并且由于正则表达式几乎总是不利于解析,所以我最终尝试了它并且它有效。

编辑:在带引号的字符串中添加了对转义引号的支持。