2

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

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

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

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

  1. 验证字符串以确保它们具有xy
  2. x实际上解析和的值y
  3. 获取字符串的其余部分。

鉴于顶部的示例,这将导致以下变量:

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

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

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

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

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

4

5 回答 5

3

假设您还想对其他 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};

在较旧的 Perl 上(至少 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{$1} = $2;
}

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

如果您需要处理更多数据,这些还​​有继续工作的额外好处。

于 2008-08-21T20:02:11.173 回答
1

我不是最擅长正则表达式,但这似乎非常接近您正在寻找的内容:

/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 = $1;
        $y = $2;
        $remainder = $4;
        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.

这当然会遗漏大量的错误检查,而且我对您的输入一无所知,但这似乎可行。

于 2008-08-14T01:56:53.480 回答
1

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

/^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 = $1;
        $y = $2;
        $remainder = $3;
        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: {}

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

于 2008-08-17T15:39:50.480 回答
1

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+) 和 y=(\w+)(?: 和 (.*))?/

这不允许 x= 和 y= 选项、places 和 allow 以及可选的 "and..." 之间的任何内容,这将是 $3

于 2008-09-15T15:20:12.470 回答
0

这基本上是我为解决这个问题所做的:

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

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

$x = $1;

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

$y = $1;

我省略了一些额外的验证和错误处理。这种技术有效,但并不像我希望的那样简洁或漂亮。我希望有人能给我更好的建议。

于 2008-08-14T00:46:38.473 回答