解析像字符串这样的HTML属性

时间:2014-10-08 20:35:58

标签: perl

拥有类似HTML属性的字符串

key1="value1 value2" key2="va3" key4

需要解析这样的字符串来获得HoA:

$parsed = {
    key1' => [
                  'value1',
                  'value2'
             ],
    key2 => [ 'val3' ],    #or key2 => 'val3'  doesn't matter..
    key4 => undef,
};

自己创建函数,例如:

#!/usr/bin/env perl

use 5.014;
use strict;
use warnings;
use Data::Dumper;

while(<DATA>) {
    my $parsed;
    chomp;
    next if m/\A\s*#/;
    while( m/(\w+)(\s*=\s*(["'])(.*?)(\3))?/g ) {
        my $k = $1;
        if( $4 ) {
            my @v = split(/\s+/, $4);
            $parsed->{$k} = \@v;
        }
        else {
            $parsed->{$k} = undef;
        }
    }
    say Dumper $parsed;
}

__DATA__
key1="value1 value2" key2 key3="val3"
key1='value1 "value2"' key8 key3='val3'
key1='value1 i\'m' key2 key3="val3"
key1='value1 value2' key8 key3=val3

工作并打印前两行的正确结果。

$VAR1 = {
          'key1' => [
                      'value1',
                      'value2'
                    ],
          'key3' => [
                      'val3'
                    ],
          'key2' => undef
        };

$VAR1 = {
          'key1' => [
                      'value1',
                      '"value2"'
                    ],
          'key3' => [
                      'val3'
                    ],
          'key8' => undef
        };

不幸的是,它在第​​3行失败 - 不知道如何处理转义的引号。 (而且只是想出key=val(没有引号)也是有效的))

另外,因为不想重新发明轮子,为此可能在CPAN上存在一些模块,只是没有任何想法搜索什么。 ;(

修改

@mpapec提出了一个模块,这对解析“assignement”的RHS部分有很大帮助。我的问题是字符串包含多个空格分隔LHS=RHS,其中RHS可以引用(单引号和双引号)或不引用(在一个值的情况下)和RHS值(在引号中)是空格也划定了..

key1="value1 value2" key2="va3" key4 key5=val5 key6='val6' key7='val x\'y zzz'

所以我不知道如何将字符串分成多个LHS=RHS部分,因为无法在空间拆分而无法使用我的正则表达式,因为它在转义失败引号。 (也许一些更复杂的正则表达式处理转义可以工作)。

有什么建议吗?

3 个答案:

答案 0 :(得分:2)

您可以使用Text::ParseWords作为mpapec建议:

use strict;
use warnings;
use 5.010;

use Data::Dumper;
use Text::ParseWords;

$Data::Dumper::Sortkeys = 1;

my $string = q{key1="value1 value2" key2="va3" key4 key5=val5 key6='val6' key7='val x\'y zzz'};

my @words = shellwords $string;

my %parsed;
foreach my $word (@words) {
    my ($key, $values) = split /=/, $word, 2;

    $parsed{$key} //= [];
    push @{ $parsed{$key} }, $_ for shellwords $values;
}

print Dumper \%parsed;

输出:

$VAR1 = {
          'key1' => [
                      'value1',
                      'value2'
                    ],
          'key2' => [
                      'va3'
                    ],
          'key4' => [],
          'key5' => [
                      'val5'
                    ],
          'key6' => [
                      'val6'
                    ],
          'key7' => [
                      'val',
                      'x\'y',
                      'zzz'
                    ]
        };

请注意,为了保持一致性,我将没有值的键分配为空数组而不是undef。我认为这将使数据结构更易于使用。

另请注意,我两次致电shellwords。我这样做是为了从转义引号中删除反斜杠,所以

key7='val x\'y zzz'

分为

val x'y zzz

而不是

val x\'y zzz

(上面输出中x\'y中的反斜杠由Data::Dumper添加;变量本身没有反斜杠。)

答案 1 :(得分:1)

要解决当前问题,您可以设置更改以特殊方式处理反斜杠。

#!/usr/bin/env perl

use 5.014;
use strict;
use warnings;
use Data::Dump;

my $parsed;

while (<DATA>) {
    chomp;
    next if m/\A\s*#/;
    while (
        m{
        (\w+)
        (?:
            \s* = \s* 
            (["'])
            ( (?: (?!\2)[^\\] | \\. )* )
            \2
        )?
    }gx
        )
    {
        my $k = $1;
        if ($2) {
            ( my $val = $3 ) =~ s/\\(.)/$1/g;    # Unescape backslashes
            $parsed->{$k} = [ split /\s+/, $val ];    # Split words
        } else {
            $parsed->{$k} = undef;
        }
    }
    dd $parsed;
    print "\n";
}

__DATA__
key1="value1 value2" key2 key3="val3"
key1='value1 "value2"' key2 key3='val3'
key1='value1 i\'m' key2 key3="val3"

输出:

{ key1 => ["value1", "value2"], key2 => undef, key3 => ["val3"] }

{ key1 => ["value1", "\"value2\""], key2 => undef, key3 => ["val3"] }

{ key1 => ["value1", "i'm"], key2 => undef, key3 => ["val3"] }

还有其他问题需要考虑,但这可能有助于您进一步发展。

答案 2 :(得分:0)

你可能会考虑基于Parser :: MGC的东西。

上的示例看起来就像一个很好的简单循环
my $key = $self->token_ident;
$self->expect( '=' );
my $value = $self->token_string;