题
这是我最近遇到的一个问题。我有以下形式的属性字符串
"x=1 and y=abc and z=c4g and ..."
有些属性具有数值,有些具有字母值,有些具有混合值,有些具有日期等。
每个字符串都是 应该 具有 ”x=someval and y=anotherval
” 一开始,但有些人没有。我需要做三件事。
- 验证字符串以确保它们具有
x
和y
. - 实际上解析值
x
和y
. - 获取字符串的其余部分。
鉴于顶部的示例,这将导致以下变量:
$x = 1;
$y = "abc";
$remainder = "z=c4g and ..."
我的问题是:有没有一种(相当)简单的方法来解析这些 和 使用单个正则表达式进行验证?IE。:
if ($str =~ /someexpression/)
{
$x = $1;
$y = $2;
$remainder = $3;
}
请注意,该字符串可能包含 仅有的 x
和 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 = $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.
这当然省略了大量的错误检查,而且我不知道有关您输入的所有信息,但这似乎有效。
其他提示
假设您还想对其他名称=值对执行某些操作,这就是我的做法(使用 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{$1} = $2;
}
# to make sure that x & y exist
die unless exists $hash{x} and exists $hash{y};
如果您需要处理更多数据,这些还有一个额外的好处,那就是可以继续工作。
作为对 Rudd 版本的相当简单的修改,
/^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 测试具有相同的限制,则字符串将失败。
Rudd 和 Cebjyre 已经帮助你实现了大部分目标,但他们都存在某些问题:
路德建议:
/x=(.+) 和 y=([^ ]+)( 和 (.*))?/
Cebjyre 将其修改为:
/^x=(.+) 和 y=([^ ]+)(?:和 (。*))?/
第二个版本更好,因为它不会将“not_x=foo”与“x=foo”混淆,但会接受诸如“x=foo z=bar y=baz”之类的内容并设置$1 =“foo z=bar”,即不受欢迎的。
这可能就是您正在寻找的:
/^x=(\w+) 和 y=(\w+)(?:和 (。*))?/
这不允许 x= 和 y= 选项之间的任何内容,放置并允许可选的“and...”,其大小为 $3
基本上这就是我为解决这个问题所做的事情:
($x_str, $y_str, $remainder) = split(/ and /, $str, 3);
if ($x_str !~ /x=(.*)/)
{
# error
}
$x = $1;
if ($y_str !~ /y=(.*)/)
{
# error
}
$y = $1;
我省略了一些额外的验证和错误处理。这种技术有效,但它并不像我希望的那样简洁或漂亮。我希望有人能给我更好的建议。