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;