Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get repeated matches with preg_match_all()

I'm trying to get all substrings matched with a multiplier:

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

This example returns, as expected, the last match in [1]:

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

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

)

However, I would like to get all strings matched by (,\d+), to get something like:

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

Is there a way to do this with a single function such as preg_match_all()?

like image 499
BenMorel Avatar asked Jul 05 '11 08:07

BenMorel


People also ask

What does preg_ match return?

The preg_match() function returns whether a match was found in a string.

How to use preg_ match_ all in PHP?

PHP preg_match_all() Function The preg_match_all() function returns the number of matches of a pattern that were found in a string and populates a variable with the matches that were found.

What is the difference between Preg_match and Preg_match_all?

preg_match stops looking after the first match. preg_match_all , on the other hand, continues to look until it finishes processing the entire string. Once match is found, it uses the remainder of the string to try and apply another match.

Has been considered the benchmark for powerful regular expression?

Perl has long been considered the benchmark for powerful regular expressions. PHP uses a C library called pcre to provide almost complete support for Perl's arsenal of regular expression features.


1 Answers

It's true that PHP (or better to say PCRE) doesn't store values of repeated capturing groups for later access (see PCRE docs):

If a capturing subpattern is matched repeatedly, it is the last portion of the string that it matched that is returned.

But in most cases the known token \G does the job. \G 1) matches the beginning of input string (as \A or ^ when m modifier is not set) or 2) starts match from where the previous match ends. Saying that, you have to use it like the following:

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

See live demo here

or if capturing group doesn't matter:

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

by which $matches will hold this (see live demo):

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

)

Note: the benefit of using \G over the other answers (like explode() or lookbehind solution or just preg_match_all('/,?\d+/', ...)) is that you are able to validate the input string to be only in the desired format ^\d+(,\d+)*$ at the same time while exporting the matches:

preg_match_all('/(?:^(?=\d+(?:,\d+)*$)|\G(?!^),)\d+/', $list, $matches);
like image 53
revo Avatar answered Oct 04 '22 00:10

revo