Парсинг атрибутов с помощью регулярных выражений в Perl

Вот проблема, с которой я столкнулся недавно. У меня есть атрибуты строки вида

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

Некоторые атрибуты имеют числовые значения, некоторые имеют альфа-значения, некоторые смешанные, некоторые имеют даты и т. Д.

Каждая строка должна иметь " x=someval and y=anotherval" в начале, но некоторые нет. У меня есть три вещи, которые мне нужно сделать.

  1. Проверьте строки, чтобы убедиться, что они есть xи y.
  2. Собственно разбери значения для xи y.
  3. Получите остаток строки.

Учитывая приведенный выше пример, это приведет к следующим переменным:

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

Мой вопрос: есть (достаточно) простой способ их проанализировать и проверить с помощью одного регулярного выражения? то есть:

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

Обратите внимание , что строка может состоять из только x и yатрибутов. Это допустимая строка.

Я опубликую свое решение как ответ, но оно не соответствует моим предпочтениям с одним регулярным выражением.

14.08.2008 00:40:26
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.

Это, конечно, исключает множество ошибок, и я не знаю все о ваших входных данных, но, похоже, это работает.

1
14.08.2008 01:56:53

Вот в основном то, что я сделал, чтобы решить эту проблему:

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

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

$x = $1;

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

$y = $1;

Я пропустил некоторые дополнительные проверки и обработки ошибок. Эта техника работает, но она не так лаконична или хороша, как хотелось бы. Я надеюсь, что у кого-то будет лучшее предложение для меня.

0
14.08.2008 00:46:38
Это выглядит для меня проще и удобнее в обслуживании, чем любое из решений «один регулярное выражение для управления ими всеми». Возможно, я бы просто добавил ^ в начале xge, чтобы соответствовать x = и y =, чтобы избежать случая not_x = ... или подобного. Почему вы хотите одно регулярное выражение?
mirod 15.07.2009 09:01:46

Как довольно простая модификация версии Радда,

/^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-теста было то же ограничение, что и для строки.

1
17.08.2008 15:39:50

Предполагая, что вы также хотите сделать что-то с другими парами имя = значение, я бы это сделал (используя 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};

Это дает дополнительное преимущество продолжения работы, если вам нужно работать с большим количеством данных.

3
15.07.2009 04:37:57
\Gуже соответствует началу строки, поэтому вы можете заменить (?:^|\G)на \G. Но лучший способ - это ввести \Gфактор в начале и переместить andв начало:\G (?: ^ | \s+ and \s+) (\w+) = (\S+)
Casimir et Hippolyte 11.04.2016 13:53:06

Радд и Себжир добрались до вас, но у них обоих есть определенные проблемы:

Радд предложил:

/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

1
15.09.2008 15:20:12