簡體   English   中英

Perl中的Regex Group:如何從正則表達式組中將元素捕獲到數組中,以匹配字符串中未知數量的/多個/變量出現?

[英]Regex Group in Perl: how to capture elements into array from regex group that matches unknown number of/multiple/variable occurrences from a string?

在Perl中,如何使用一個正則表達式分組來捕獲多個匹配它的事件到多個數組元素?

例如,對於字符串:

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

我會用什么作為正則表達式?

我想在這里匹配的東西之間的共性是一個賦值字符串模式,所以類似於:

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

其中*表示與該組匹配的一個或多個事件。

(我使用split()打折,因為有些匹配本身包含空格(即var3 ...),因此不會產生預期的結果。)

有了上面的正則表達式,我只得到:

0: var1=100 var2

正則表達式是否可能? 還是需要添加代碼?

在查找“perl regex multiple group”但沒有足夠的線索時,已經查看了現有的答案:

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標志意味着你可以多次將正則表達式應用於字符串。 第二次它將繼續匹配最后一個匹配在字符串中結束的位置。

現在對於正則表達式: (?:^|\\s+)匹配字符串的開頭或一個或多個空格的組。 這是必需的,所以當下次應用正則表達式時,我們將跳過鍵/值對之間的空格。 ?:表示括號內容不會被捕獲為組(我們不需要空格,只需要鍵和值)。 \\S+匹配變量名稱。 然后我們跳過任意數量的空格和兩者之間的等號。 最后, ("[^"]*"|\\S*)/匹配兩個引號,其中包含任意數量的字符,或者該值的任意數量的非空格字符。請注意,引用匹配非常脆弱並且贏了正確處理escpaped引號,例如"\\"quoted\\""將導致"\\"

編輯:

既然你真的想得到整個作業,而不是單個鍵/值,這里有一個單行提取:

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

使用正則表達式,使用我喜歡稱為彈力和伸展的技術:錨定在你知道將要存在的特征(大頭釘)上,然后抓住(拉伸)之間的內容。

在這種情況下,您知道單個分配匹配

\b\w+=.+

你在$string重復了很多這些。 請記住\\b表示單詞邊界:

單詞邊界( \\b )是兩個字符之間的一個點,在它的一邊有一個\\w ,另一邊有一個\\W (按任意順序),計算虛構字符的開頭和結尾。字符串匹配\\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";
}

輸出:

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 如何制作解析器?

#!/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。 請注意double var3,如果您希望后一個賦值覆蓋第一個,您可以使用哈希來存儲值,然后再使用它們。

PPS。 我的第一個想法是分裂'=',但是如果一個字符串包含'='並且因為正則表達式幾乎總是對解析不好,那么會失敗,所以我最終嘗試了它並且它有效。

編輯:添加了對帶引號字符串內的轉義引號的支持。

我最近不得不解析x509證書“主題”行。 它們的形式與您提供的形式類似:

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

正則表達式的簡短描述:

(\\w+\\=.+?) - 捕獲后跟'='的單詞以及非貪婪模式下的任何后續符號
(?=(?:, \\w+\\=|$)) - 后跟另一個, KEY=val或行尾。

使用正則表達式的有趣部分是:

  • .+? - 非貪婪模式
  • (?: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解決方案或其他代碼。 這是一個(大多數)非正則表達式解決方案,僅使用核心模塊。 唯一的正則表達式是\\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

如果你真的想要一個正則表達式的解決方案,艾倫摩爾的評論鏈接到他在IDEone上的代碼就是天然氣!

使用正則表達式可以做到這一點,但它很脆弱。

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM