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

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

"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 ..."

У меня вопрос: есть (достаточно) простой способ проанализировать эти и проверки с помощью одного регулярного выражения? i.e.:

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

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

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

Ответы [ 5 ]

3 голосов
/ 22 августа 2008

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

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

1 голос
/ 15 сентября 2008

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

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

/ 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 голос
/ 14 августа 2008

Я не лучший в регулярных выражениях, но это выглядит довольно близко к тому, что вы ищете:

/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 голос
/ 17 августа 2008

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

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

0 голосов
/ 14 августа 2008

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

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

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

$x = $1;

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

$y = $1;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...