在Perl中使用正则表达式解析属性
这是我最近遇到的一个问题。我有形式的属性字符串
"x=1 and y=abc and z=c4g and ..."
一些属性具有数字值,一些属性具有alpha值,一些属性具有混合值,一些属性具有日期,等等。
每个字符串的开头都应该有"x = someval和y = anotherval
",但有些则没有。我需要做三件事。
- 验证字符串以确保它们具有
x
和y
。 - 实际上解析" 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中的可选"和..."之间进行任何操作