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

StackOverflow https://stackoverflow.com/questions/10533

  •  08-06-2019
  •  | 
  •  

Вопрос

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

"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 атрибуты.Это действительная строка.

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

Это было полезно?

Решение

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

/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};

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

В качестве довольно простой модификации версии Радда:

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

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

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

/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;

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top