Как захватить каждый матч повторяющегося шаблона?
У меня есть следующее регулярное выражение:
/xxx ([a-z]+)(?:, ([a-z]+))* xxx/
Я хочу захватить все цвета в следующей тестовой строке:
xxx red, blue, pink, purple xxx
(теперь только красный и фиолетовый попадают в плен)
откройте этот URL и просмотрите соответствующие группы: http://www.regex101.com/r/oZ2cH4
Я прочитал следующее http://www.regular-expressions.info/captureall.html но трюк не сработал
(или возможно я сделал это неправильно)
как я могу решить это?
заранее спасибо
2 ответа
Возможно, вы захотите вернуть подходящую группу на основе совпадений предыдущего шаблона:
$word = '[a-z]+';
$sep = '[, ]+';
$words = $captures("~($word)(?:{$sep})?~");
$of = $captures("~xxx ({$word}(?:{$sep}{$word})*) xxx~");
print_r($words($of($subject)));
Выход:
Array
(
[0] => red
[1] => blue
[2] => pink
[3] => purple
)
В то время как $captures
это функция, которая возвращает предварительно настроенный preg_match_all
вызов, позволяющий обрабатывать не только строку как тему, но и все остальное foreach
может работать на:
$captures = function ($pattern, $group = 1) {
return function ($subject) use ($pattern, $group) {
if (is_string($subject)) {
$subject = (array)$subject;
}
$captures = [];
foreach ($subject as $step) {
preg_match_all($pattern, $step, $matches);
$captures = array_merge($captures, $matches[$group]);
}
return $captures;
};
};
По умолчанию, как показано в примере выше, возвращается первая группа (1), но это можно настроить.
Это позволяет сначала сопоставить внешний шаблон ($of
), а затем на каждом из них совпадает внутренний шаблон ($words
). Пример полностью:
$subject = '/xxx red, blue, pink, purple xxx/';
$captures = function ($pattern, $group = 1) {
return function ($subject) use ($pattern, $group) {
if (is_string($subject)) {
$subject = (array)$subject;
}
$captures = [];
foreach ($subject as $step) {
preg_match_all($pattern, $step, $matches);
$captures = array_merge($captures, $matches[$group]);
}
return $captures;
};
};
$word = '[a-z]+';
$sep = '[, ]+';
$seq = "";
$words = $captures("~($word)(?:{$sep})?~");
$of = $captures("~xxx ({$word}(?:{$sep}{$word})*) xxx~");
print_r($words($of($subject)));
Смотрите live-демо.
В учебнике "Повторение захвата группы против захвата повторяющейся группы" (by регулярно-expressions.info) описывается, как можно захватить весь контент "красный, синий, розовый, фиолетовый" в одном захвате. Шаблон, который он предложил бы,
/xxx ((?:[a-z]+(?:, )?)+) xxx/
но если бы это было действительно то, чего вы пытались достичь, вы также можете использовать более простое выражение
/xxx ([a-z, ]*) xxx/
Я подозреваю, что вы действительно хотите, чтобы захватить каждый цвет в отдельности. Это может быть лучше всего достигнуто путем захвата всего списка один раз, а затем синтаксического анализа захваченного контента.