Получить повторные совпадения с помощью preg_match_all () - PullRequest
10 голосов
/ 05 июля 2011

Я пытаюсь сопоставить все подстроки с множителем:

$list = '1,2,3,4';
preg_match_all('|\d+(,\d+)*|', $list, $matches);
print_r($matches);

Этот пример возвращает, как и ожидалось, последнее совпадение в [1]:

Array
(
    [0] => Array
        (
            [0] => 1,2,3,4
        )

    [1] => Array
        (
            [0] => ,4
        )

)

Однако я бы хотел, чтобы все строки соответствовали (,\d+), чтобы получить что-то вроде:

Array
(
    [0] => ,2
    [1] => ,3
    [2] => ,4
)

Есть ли способ сделать это с помощью одной функции, такой как preg_match_all()?

Ответы [ 6 ]

8 голосов
/ 21 июля 2011

Согласно Коби (см. Комментарии выше):

PHP не поддерживает захват одной и той же группы

Поэтому этот вопрос имеетнет решения.

2 голосов
/ 18 июня 2014

Использование lookbehind - это способ выполнить работу:

$list = '1,2,3,4';
preg_match_all('|(?<=\d),\d+|', $list, $matches);
print_r($matches);

Все ,\d+ находятся в группе 0.

вывод:

Array
(
    [0] => Array
        (
            [0] => ,2
            [1] => ,3
            [2] => ,4
        )
)
1 голос
/ 08 марта 2019

Это правда, что PHP (или, лучше сказать, PCRE) не хранит значения повторяющихся групп захвата для последующего доступа (см. Документы PCRE ):

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

Но в большинстве случаев известный токен \G выполняет свою работу. \G 1) соответствует началу входной строки (как \A или ^, когда модификатор m не установлен) или 2) начинает совпадение с того места, где заканчивается предыдущее совпадение. Сказав это, вы должны использовать его следующим образом:

preg_match_all('/^\d+|\G(?!^)(,?\d+)\K/', $list, $matches);

Смотрите живое демо здесь

или если группа захвата не имеет значения:

preg_match_all('/\G,?\d+/', $list, $matches);

, с помощью которого $matches будет содержать это (см. live демо ):

Array
(
    [0] => Array
        (
            [0] => 1
            [1] => ,2
            [2] => ,3
            [3] => ,4
        )

)

Примечание : преимущество использования \G по сравнению с другими ответами (например, explode() или решением lookbehind или просто preg_match_all('/,?\d+/', ...)) заключается в том, что вы можете проверять входную строку только на желаемый формат ^\d+(,\d+)*$ одновременно при экспорте совпадений:

preg_match_all('/(?:^(?=\d+(?:,\d+)*$)|\G(?!^),)\d+/', $list, $matches);
0 голосов
/ 17 июня 2014

С http://www.php.net/manual/en/regexp.reference.repetition.php:

При повторном захвате подшаблона полученное значение является подстрокой, которая соответствует последней итерации.

Также похожая тема:

Как получить все захваты совпадений в подгруппах с помощью preg_match_all ()?

0 голосов
/ 18 сентября 2011

Разделение - это опция, только когда символ разделения не используется в шаблонах для соответствия самому себе.У меня была ситуация, когда плохо отформатированная строка, разделенная запятыми, должна быть разбита на любой из ряда известных параметров.

то есть параметры '1,2', '2', '2,3' subject '1, 2,3 '.

Разделение на', 'приведет к' 1 ',' 2 'и' 3 ';только одно ('2') является допустимым соответствием, это происходит потому, что разделитель также является частью параметров.

Наивное регулярное выражение будет выглядеть примерно так: '~ ^ (1,2 | 2 | 2, 3) (?:, (1,2 | 2 | 2,3)) * $ ~ i ', но это сталкивается с проблемой захвата одной и той же группы.

Моим «решением» было просторазверните регулярное выражение, чтобы найти максимально возможное количество совпадений: '~ ^ (1,2 | 2 | 2,3) (?:, (1,2 | 2 | 2,3))? (?:, (1,2 | 2 | 2,3))? $ ~ I '(если доступно больше опций, просто повторите бит' (?:, (1,2 | 2 | 2,3))? '. Это приведет к пустому значениюстроковые результаты для «неиспользуемых» совпадений.

Это не самое чистое решение, но работает, когда вам приходится иметь дело с плохо отформатированными входными данными.

0 голосов
/ 05 июля 2011

Почему бы просто:

$ar = explode(',', $list);
print_r($ar);
...