2008-08-14 5 views
2

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

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

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

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

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

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

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

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

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

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

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

ответ

1

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

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

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

0

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

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

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

$x = $1; 

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

$y = $1; 

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

+0

Это выглядит мне проще и удобнее, чем любое из «одного регулярного выражения», чтобы управлять ими все "решения. Я мог бы просто добавить a^в начале theregexps для соответствия x = и y =, чтобы избежать случая not_x = ... или аналогичного. Зачем вам нужно одно регулярное выражение? – mirod 2009-07-15 09:01:46

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: {} 

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

3

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

На старых Перлз (по крайней мере, 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}; 

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

+0

+1 хороший пример названных буферов захвата! – 2012-12-18 19:13:27

1

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

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

/x=(.+) и у = ([^] + (. и (*)?))/

Cebjyre модифицирована его:

/^x=(.+) и у = ([^] +) (?: и (. *))?/

Вторая версия лучше, потому что она не будет путать «not_x = foo» с «x = foo», но будет принимать такие вещи, как «x = foo z = bar y = baz» и установить $ 1 = "foo z = bar ", что нежелательно.

Это, вероятно, то, что вы ищете: (. ?: и (*))?

/^ х = (\ W +) и у = (\ W +)/

Это не разрешает ничего между параметрами x = и y =, местами и позволяет и необязательно «и ...», которые будут в $ 3

Смежные вопросы