Анализ атрибутов с помощью регулярного выражения в Perl
Вот проблема, с которой я столкнулся недавно. У меня есть строки атрибутов вида
"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 ..."
Мой вопрос: есть ли (разумно) простой способ проанализировать их и проверить с помощью одного регулярного выражения? то есть:
if ($str =~ /someexpression/)
{
$x = $1;
$y = $2;
$remainder = $3;
}
Обратите внимание , что строка может состоять из только x
и y
атрибутов. Это допустимая строка.
Я отправлю свое решение в качестве ответа, но оно не соответствует моим предпочтениям с одним регулярным выражением.
Ответов (5)5
Я не лучший специалист в области регулярных выражений, но это кажется довольно близким к тому, что вы ищете:
/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.
При этом, конечно, не учитывается множество проверок ошибок, и я не знаю всего о ваших входных данных, но, похоже, это работает.
Радд и Себджайре в большинстве случаев довели вас до конца, но у них обоих есть определенные проблемы:
Радд предложил:
/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 =, местами и допусками и необязательным "и ...", которое будет в $ 3.
Вот в основном то, что я сделал, чтобы решить эту проблему:
($x_str, $y_str, $remainder) = split(/ and /, $str, 3);
if ($x_str !~ /x=(.*)/)
{
# error
}
$x = $1;
if ($y_str !~ /y=(.*)/)
{
# error
}
$y = $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-тест имел такое же ограничение, что и строка не прошла бы.
Предполагая, что вы также хотите что-то сделать с другими парами имя = значение, я бы сделал это следующим образом (используя 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};
У них есть дополнительное преимущество в виде продолжения работы, если вам нужно работать с большим количеством данных.