在Perl中使用正则表达式解析属性

时间:2020-03-05 18:39:31  来源:igfitidea点击:

这是我最近遇到的一个问题。我有形式的属性字符串

"x=1 and y=abc and z=c4g and ..."

一些属性具有数字值,一些属性具有alpha值,一些属性具有混合值,一些属性具有日期,等等。

每个字符串的开头都应该有"x = someval和y = anotherval",但有些则没有。我需要做三件事。

  • 验证字符串以确保它们具有xy
  • 实际上解析" x"和" y"的值。
  • 获取其余的字符串。

给定顶部的示例,这将导致以下变量:

$x = 1;
$y = "abc";
$remainder = "z=c4g and ..."

我的问题是:是否存在(合理)简单的方法来解析这些内容并使用单个正则表达式进行验证? IE。:

if ($str =~ /someexpression/)
{
    $x = ;
    $y = ;
    $remainder = ;
}

请注意,字符串只能包含x和y属性。这是一个有效的字符串。

我将发布解决方案作为答案,但它不符合我的单正则表达式首选项。

解决方案

回答

这基本上是我为解决此问题所做的工作:

($x_str, $y_str, $remainder) = split(/ and /, $str, 3);

if ($x_str !~ /x=(.*)/)
{
    # error
}

$x = ;

if ($y_str !~ /y=(.*)/)
{
    # error
}

$y = ;

我省略了一些其他的验证和错误处理。这项技术有效,但是不像我想要的那么简洁。我希望有人会对我有更好的建议。

回答

我不是最擅长使用正则表达式,但这似乎与我们要查找的内容非常接近:

/x=(.+) and y=([^ ]+)( and (.*))?/

除了使用$ 1,$ 2和$ 4. 正在使用:

my @strs = ("x=1 and y=abc and z=c4g and w=v4l",
            "x=yes and y=no",
            "z=nox and w=noy");

foreach (@strs) {
    if ($_ =~ /x=(.+) and y=([^ ]+)( and (.*))?/) {
        $x = ;
        $y = ;
        $remainder = ;
        print "x: $x; y: $y; remainder: $remainder\n";
    } else {
        print "Failed.\n";
    }
}

输出:

x: 1; y: abc; remainder: z=c4g and w=v4l
x: yes; y: no; remainder: 
Failed.

当然,这省去了很多错误检查,而且我对输入一无所知,但这似乎行得通。

回答

作为对陆克文版本的相当简单的修改,

/^x=(.+) and y=([^ ]+)(?: and (.*))?/

将允许我们使用$ 1,$ 2和$ 3(?:使它成为一个非捕获组),并确保字符串以" x ="开头,而不是允许" not_x ="匹配

如果我们对x和y值有更好的了解,则应使用此方法进一步加紧正则表达式:

my @strs = ("x=1 and y=abc and z=c4g and w=v4l",
        "x=yes and y=no",
        "z=nox and w=noy",
        "not-x=nox and y=present",
        "x=yes and w='there is no and y=something arg here'");

foreach (@strs) {
    if ($_ =~ /^x=(.+) and y=([^ ]+)(?: and (.*))?/) {
        $x = ;
        $y = ;
        $remainder = ;
        print "x: {$x}; y: {$y}; remainder: {$remainder}\n";
    } else {
        print "$_ Failed.\n";
    }
}

输出:

x: {1}; y: {abc}; remainder: {z=c4g and w=v4l}
x: {yes}; y: {no}; remainder: {}
z=nox and w=noy Failed.
not-x=nox and y=present Failed.
x: {yes and w='there is no}; y: {something}; remainder: {}

请注意,如果x测试具有与字符串失败相同的限制,则最后一个测试的缺失部分是由于y测试的当前版本不需要空格。

回答

假设我们还想对其他name = value对进行操作,这就是我的操作方法(使用Perl 5.10版):

use 5.10.0;
use strict;
use warnings;

my %hash;
while(
    $string =~ m{
       (?: ^ | \G )    # start of string or previous match
       \s*

       (?<key>   \w+ ) # word characters
       =
       (?<value> \S+ ) # non spaces

       \s*             # get to the start of the next match
       (?: and )?
    }xgi
){
    $hash{$+{key}} = $+{value};
}

# to make sure that x & y exist
die unless exists $hash{x} and exists $hash{y};

在较旧的Perls上(至少为Perl 5.6);

use strict;
use warnings;

my %hash;
while(
    $string =~ m{
       (?: ^ | \G )   # start of string or previous match
       \s*

       ( \w+ ) = ( \S+ )

       \s*            # get to the start of the next match
       (?: and )?
    }xgi
){
    $hash{} = ;
}

# to make sure that x & y exist
die unless exists $hash{x} and exists $hash{y};

如果我们需要处理更多数据,那么它们具有继续工作的额外好处。

回答

陆克文(Rudd)和塞比耶(Cebjyre)已经为我们提供了大部分帮助,但是他们都有一些问题:

陆克文建议:

/x=(.+) and y=([^ ]+)( and (.*))?/

Cebjyre修改为:

/^x=(.+) and y=([^ ]+)(?: and (.*))?/

第二个版本更好,因为它不会将" not_x = foo"与" x = foo"混淆,但是会接受诸如" x = foo z = bar y = baz"之类的东西,并设置$ 1 =" foo z = bar"不合要求的。

这可能是我们要寻找的:

/^x=(\w+) and y=(\w+)(?: and (.*))?/

这不允许在x =和y =选项,位置和允许之间以及在$ 3中的可选"和..."之间进行任何操作