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

1

Хорошо, это действительно сложно объяснить на английском, поэтому я просто приведу пример.

Я буду иметь строки в следующем формате:

key-value;key1-value;key2-...

и мне нужно извлечь данные как массив

array('key'=>'value','key1'=>'value1', ... )

Я планировал использовать regexp для достижения (большей части) этой функции и написал это регулярное выражение:

/^(\w+)-([^-;]+)(?:;(\w+)-([^-;]+))*;?$/

для работы с preg_match и этим кодом:

for ($l = count($matches),$i = 1;$i<$l;$i+=2) {
    $parameters[$matches[$i]] = $matches[$i+1];
}

Однако regexp явно возвращает только 4 обратных ссылок - первую и последнюю пары ключ-значение входной строки. Есть ли способ обойти это? Я знаю, что я могу использовать регулярное выражение только для проверки правильности строки и использования PHP explode в циклах с отличными результатами, но мне действительно интересно, возможно ли это с помощью регулярных выражений.

Короче говоря, мне нужно зафиксировать произвольное число этих пар key-value; в строке с помощью регулярных выражений.

Теги:
preg-match

6 ответов

2
Лучший ответ

Вы можете использовать lookahead для проверки ввода при извлечении совпадений:

/\G(?=(?:\w++-[^;-]++;?)++$)(\w++)-([^;-]++);?/

(?=(?:\w++-[^;-]++;?)++$) является частью проверки. Если вход недействителен, совпадение будет сбой немедленно, но просмотр всегда будет оцениваться каждый раз, когда применяется регулярное выражение. Чтобы сохранить его (вместе с остальным регулярным выражением) в синхронизации с парами ключ-значение, я использовал \G для привязки каждого соответствия к тому месту, где закончилось предыдущее совпадение.

Таким образом, если lookahead будет успешным в первый раз, он гарантирует успешное выполнение каждого последующего времени. Очевидно, что это не так эффективно, как могло бы быть, но это, вероятно, не будет проблемой - только ваше тестирование может точно сказать.

Если сбой просмотра невозможен, preg_match_all() будет возвращать ноль (false). Если это удастся, совпадения будут возвращены в массиве массивов: один для полных пар ключ-значение, один для ключей, один для значений.

2

Используйте preg_match_all(). Может быть, что-то вроде:

$matches = $parameters = array();
$input = 'key-value;key1-value1;key2-value2;key123-value123;';

preg_match_all("/(\w+)-([^-;]+)/", $input, $matches, PREG_SET_ORDER);

foreach ($matches as $match) {
   $parameters[$match[1]] = $match[2];
}

print_r($parameters);

EDIT:

чтобы сначала проверить, соответствует ли входная строка шаблону, а просто используйте:

if (preg_match("/^((\w+)-([^-;]+);)+$/", $input) > 0) {
    /* do the preg_match_all stuff */
}       

EDIT2: конечная точка с запятой необязательна

if (preg_match("/^(\w+-[^-;]+;)*\w+-[^-;]+$/", $input) > 0) {
    /* do the preg_match_all stuff */
}       
  • 0
    Так как это единственный ответ в ответ на мой вопрос, я даю вам + и приму его, если никто не предложит лучшего решения, но регулярное выражение не проверяет данную строку ( 'foo-bar-baz' будет считаться действительным ценности)
  • 0
    так что для 'foo-bar-baz' вы хотите 'foo' => 'bar-baz' или 'foo-bar' => 'baz' ? я могу легко дать вам альтернативное регулярное выражение;)
Показать ещё 7 комментариев
2

regex - мощный инструмент, но иногда его не лучший подход.

$string = "key-value;key1-value";
$s = explode(";",$string);
foreach($s as $k){
    $e = explode("-",$k);
    $array[$e[0]]=$e[1];
}
print_r($array);
  • 0
    Спасибо, но, как я сказал в OP: I know I can use regex just to test the correctness of the string and use PHP's explode in loops with perfect results
  • 0
    Итак ... вы знаете, что это работает, но вы не хотите использовать его, потому что вы бы предпочли использовать регулярное выражение, даже если регулярное выражение не является подходящим инструментом для работы. Является ли регулярное выражение реальным требованием, или вы просто пытаетесь сделать это таким образом, потому что оно должно работать?
Показать ещё 1 комментарий
0

Я не думаю, что вы можете выполнять проверку и извлечение данных с помощью одного единственного регулярного выражения, поскольку для проверки необходимы привязки (^ и $) и preg_match_all() для данных, но если вы используете привязки с помощью preg_match_all() он вернет только последний сопоставленный набор.

0

как насчет этого решения:

$samples = array(
    "good" => "key-value;key1-value;key2-value;key5-value;key-value;",
    "bad1" => "key-value-value;key1-value;key2-value;key5-value;key-value;",
    "bad2" => "key;key1-value;key2-value;key5-value;key-value;",
    "bad3" => "k%ey;key1-value;key2-value;key5-value;key-value;"
);

foreach($samples as $name => $value) {
    if (preg_match("/^(\w+-\w+;)+$/", $value)) {
        printf("'%s' matches\n", $name);
    } else {
        printf("'%s' not matches\n", $name);
    }
}
  • 0
    Последняя точка с запятой ; не требуется. Также это только проверяет ввод, я хотел бы, чтобы регулярное выражение проверяло И создавало массив.
  • 0
    да, я не поняла, что вам тоже нужны данные.
0

Нет. Новые совпадения перезаписывают старые совпадения. Возможно, аргумент limit explode() был бы полезен при взрыве.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню