Perl中的正则expression式组:如何从正则expression式组中捕获元素,匹配来自string的未知数/多个/variables出现的元素?

在Perl中,如何使用一个正则expression式分组来捕获多个匹配它的事件,并将它们分成几个数组元素?

例如,对于一个string:

var1=100 var2=90 var5=hello var3="a, b, c" var7=test var3=hello 

用代码处理:

 $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"; } 

我希望看到作为输出:

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

我会用什么作为正则expression式?

我想在这里匹配的东西之间的共同点是一个赋值string模式,所以像这样:

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

*表示与该组匹配的一个或多个匹配项。

(我打折使用split(),因为一些匹配包含自己内部的空间(即var3 …),因此不会给出所需的结果。)

有了上面的正则expression式,我只能得到:

 0: var1=100 var2 

在正则expression式中可能吗? 或者需要添加代码?

看看现有的答案,当search“perl正则expression式多个组”,但没有足够的线索:

  • 处理多个logging中的多个捕获组
  • 正则expression式组中的多个匹配?
  • 正则expression式:重复捕获组
  • 正则expression式匹配和分组
  • 我如何正则匹配与未知组数量的分组
  • awk从每一行中提取多个组
  • 匹配多个正则expression式组并删除它们
  • Perl:在符合某个标准的地方删除多个reccuring行
  • 正则expression式每行匹配成多个组?
  • PHP RegEx分组多个匹配
  • 如何find正则expression式组的多个事件?
 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"; } 

打印:

 <var1> => <100> <var2> => <90> <var5> => <hello> <var3> => <"a, b, c"> <var7> => <test> <var3> => <hello> 

说明:

最后一块:最后的g标志意味着你可以多次应用正则expression式到string。 第二次它将继续匹配最后一场比赛结束的string。

现在对于正则expression式: (?:^|\s+)匹配string的开头或一个或多个空格的组。 这是需要的,所以当下一次应用正则expression式时,我们将跳过键/值对之间的空格。 ?:表示圆括号内容不会被作为组捕获(我们不需要空格,只有键和值)。 \S+匹配variables名称。 然后我们跳过任何数量的空格和等号。 最后, ("[^"]*"|\S*)/匹配两个引号之间的任意数量的字符,或匹配任意数量的非空格字符。请注意,引号匹配非常脆弱,不正确处理保留的引号,例如"\"quoted\""会导致"\"

编辑:

既然你真的想得到整个任务,而不是单个键/值,这里是一个提取这些:

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

使用正则expression式,使用一种我喜欢称之为“拉伸 – 拉伸”的技巧:锚定您知道的特征(粘性),然后抓住(拉伸)之间的内容。

在这种情况下,你知道一个分配匹配

 \b\w+=.+ 

而且你在$string有很多这样的重复。 请记住, \b意思是字边界:

单词边界( \b )是两个字符之间的一个点,它的一边有一个\w ,另一边有一个\W (以任意顺序),计算出虚拟字符的开始和结束string匹配\W

赋值中的值用正则expression式来描述可能有点棘手,但是你也知道每个值都将以空白结束 – 尽pipe不一定是遇到的第一个空白符号 – 后面跟着另一个赋值或string结尾。

为了避免重复断言模式,可以使用qr//编译一次qr//并在模式中重复使用它,同时使用预读断言(?=...)来将匹配拉伸到足以捕获整个值的同时也阻止它从溢出到下一个variables名称。

m//g列表上下文中的模式匹配给出了以下行为:

/g修饰符指定全局模式匹配,即在string内尽可能匹配。 它的performance取决于上下文。 在列表上下文中,它将返回正则expression式中任何捕获括号所匹配的子string列表。 如果没有括号,则返回所有匹配的string的列表,就好像在整个模式中有括号一样。

模式$assignment使用非贪婪.+? 在预见到另一项任务或行尾时立即切断价值。 请记住,匹配返回来自所有捕获子模式的子string,所以预见的交替使用非捕获(?:...)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"; } 

输出:

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

我不是说这是你应该做的,但是你要做的是写一个语法 。 现在你的例子对于语法来说简单,但是Damian Conway的Regexp :: Grammars模块在这方面真的很棒。 如果你一定要成长起来,你会发现它会让你的生活变得更轻松。 我在这里使用它很多 – 这是一种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 {%/}; 

输出:

 $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' } ] 

也许是顶一下,但我可以看看http://p3rl.org/Parse::RecDescent 。 如何做一个parsing器?

 #!/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); 

收益率:

 var1 => 100 var2 => 90 var5 => hello var3 => "a, b, c" var7 => test var8 => " haha \" heh " var3 => hello 

PS。 注意双var3,如果你想要后面的分配覆盖第一个你可以使用散列来存储值,然后使用它们。

PPS。 我的第一个想法是在'='上分割,但是如果一个string包含'=',并且因为regexps几乎总是不好分析,那么我会结束尝试它并且工作。

编辑:添加了对引用string中的转义引号的支持。

我最近不得不parsingx509证书“主题”行。 他们的forms与你所提供的相似:

 echo 'Subject: C=HU, L=Budapest, O=Microsec Ltd., CN=Microsec e-Szigno Root CA 2009/emailAddress=info@e-szigno.hu' | \ perl -wne 'my @a = m/(\w+\=.+?)(?=(?:, \w+\=|$))/g; print "$_\n" foreach @a;' C=HU L=Budapest O=Microsec Ltd. CN=Microsec e-Szigno Root CA 2009/emailAddress=info@e-szigno.hu 

正则expression式的简短描述:

(\w+\=.+?) – 在非贪婪模式下捕捉字符后加'='和后续符号
(?=(?:, \w+\=|$)) – 其后是另一个, KEY=val或行尾。

使用的正则expression式的有趣的部分是:

  • .+? – 非贪婪模式
  • (?:pattern) – 非捕捉模式
  • (?=pattern)零宽度正面预见断言

这个将为您提供双引号的常见转义,例如var3 =“a,\”b,c“。

 @a = /(\w+=(?:\w+|"(?:[^\\"]*(?:\\.[^\\"]*)*)*"))/g; 

在行动:

 echo 'var1=100 var2=90 var42="foo\"bar\\" var5=hello var3="a, b, c" var7=test var3=hello' | perl -nle '@a = /(\w+=(?:\w+|"(?:[^\\"]*(?:\\.[^\\"]*)*)*"))/g; $,=","; print @a' var1=100,var2=90,var42="foo\"bar\\",var5=hello,var3="a, b, c",var7=test,var3=hello 
 #!/usr/bin/perl use strict; use warnings; use Text::ParseWords; use YAML; my $string = "var1=100 var2=90 var5=hello var3=\"a, b, c\" var7=test var3=hello"; my @parts = shellwords $string; print Dump \@parts; @parts = map { { split /=/ } } @parts; print Dump \@parts; 

您要求RegEx解决scheme或其他代码。 这里是一个(主要)非正则expression式解决scheme,只使用核心模块。 唯一的正则expression式是\s+来确定分隔符; 在这种情况下是一个或多个空间。

 use strict; use warnings; use Text::ParseWords; my $string="var1=100 var2=90 var5=hello var3=\"a, b, c\" var7=test var3=hello"; my @array = quotewords('\s+', 0, $string); for ( my $i = 0; $i < scalar( @array ); $i++ ) { print $i.": ".$array[$i]."\n"; } 

或者你可以在这里执行代码

输出是:

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

如果你真的想要一个正则expression式的解决scheme,艾伦·摩尔(Alan Moore)在IDE中链接到他的代码的评论就是气!

正则expression式可以做到这一点,但它是脆弱的。

 my $string = "var1=100 var2=90 var5=hello var3=\"a, b, c\" var7=test var3=hello"; my $regexp = qr/( (?:\w+=[\w\,]+) | (?:\w+=\"[^\"]*\") )/x; my @matches = $string =~ /$regexp/g;