Регекс не соответствует, жадность

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

Я пытаюсь получить +123 (если cd: существует, как в первой строке) и 456,

<?php

$data[] = 'longstring start waste cd:+123yz456z longstring';
$data[] = 'longstring start waste +yz456z longstring';
$regexs[] = '/start[^z]*?(cd:([^y]+)y)?[^z]*z([^z]*)z/'; // first
$regexs[] = '/start[^z]*?(cd:([^y]+)y)[^z]*z([^z]*)z/';  // second

foreach ($regexs as $regex) {
  foreach ($data as $string) {
    if (preg_match($regex, $string, $match)) {
      echo "Tried '$regex' on '$string' and got " . implode(',', array_split($match, 1));
      echo "\n";
    }
  }
}
?>

Выход:

Tried '/start[^z]*?(cd:([^y]+)y)?[^z]*z([^z]*)z/' on 'longstring start waste cd:+123yz456z longstring' and got ,,456
Tried '/start[^z]*?(cd:([^y]+)y)?[^z]*z([^z]*)z/' on 'longstring start waste +yz456z longstring' and got ,,456
Tried '/start[^z]*?(cd:([^y]+)y)[^z]*z([^z]*)z/' on 'longstring start waste cd:+123yz456z longstring' and got cd:+123y,+123,456

Там нет четвертой линии с cd: нет во второй строке.

Ожидаемый результат (так как я не эксперт), где первая строка отличается от фактического результата:

Tried '/start[^z]*?(cd:([^y]+)y)?[^z]*z([^z]*)z/' on 'longstring start waste cd:+123yz456z longstring' and got cd:+123y,+123,456
Tried '/start[^z]*?(cd:([^y]+)y)?[^z]*z([^z]*)z/' on 'longstring start waste +yz456z longstring' and got ,,456
Tried '/start[^z]*?(cd:([^y]+)y)[^z]*z([^z]*)z/' on 'longstring start waste cd:+123yz456z longstring' and got cd:+123y,+123,456

1 ответ

Решение

Итак, вы хотите захватить +123 если есть cd:, и всегда 456? Вот как бы я это сделал:

$data[] = 'longstring start waste cd:+123yz456z longstring';
$data[] = 'longstring start waste +yz456z longstring';

$regexs[] = '/start.+?(?:cd:(.+?)y)?.*?z(.+?)z/';

С либеральным употреблением не жадных (?) множители, вы можете заставить его делать именно то, что вы хотите.

Также обратите внимание на (?:) группа без захвата. Они очень полезны.

РЕДАКТИРОВАТЬ Очевидно, что это не работает, давайте попробуем другой подход, с группой "или / или":

$regexs[] = '/start.+?(?:cd:(.+?)yz(.+?)z|\+yz(.+?)z)/';
Другие вопросы по тегам