parker.sikand
parker.sikand

Reputation: 1381

php recursive regex

This is a simple problem but I'm wasting too much time trying to figure it out on my own.

I had data stored in strings with the pattern r{N}-{N}s{N} where {N} is a one or two digit number. This pattern can repeat up to 5 times. I need to extract the numbers from the pattern, preferably grouped somehow.

For example, if the string were "r4-10s5", I want to extract {4,10,5}

If the string were "r4-10s5r6-7s8" I want to extract {4,10,5} {6,7,8}

How could I get this done in PHP?

I have tried using recursive regular expressions but I cannot get them to match properly.

Upvotes: 0

Views: 80

Answers (3)

Sverri M. Olsen
Sverri M. Olsen

Reputation: 13283

This is how I would do it:

function extractNumbers($string, $multiples = 3, $strict = false) {
    $string = preg_replace('/\D+/', ' ', trim($string));
    $array  = explode(' ', trim($string));
    $count  = count($array);
    $remove = ($count % $multiples);
    if ($remove > 0) {
        // Strict, do not allow dangling numbers
        if ($strict) {
            return null;
        }
        $array = array_slice($array, 0, ($count - $remove));
    }
    $array = array_map('intval', $array);
    return array_chunk($array, $multiples);
}
$str = 'r4-10s5r6-7s8';
var_dump(extractNumbers($str));

It may not be very elegant, but it is relatively easy to understand and maintain.

Upvotes: -1

Supericy
Supericy

Reputation: 5896

$str = "r4-10s5r6-7s8";

preg_match_all("#r(\d{1,2})-(\d{1,2})s(\d{1,2})#", $str, $matches);

// group the matches together
$sets = array();
for ($i = 0, $count = count($matches[0]); $i < $count; $i++)
    $sets[] = array($matches[1][$i], $matches[2][$i], $matches[3][$i]);

print_r($sets);

Outputs:

array (size=2)
  0 => 
    array (size=3)
      0 => string '4' (length=1)
      1 => string '10' (length=2)
      2 => string '5' (length=1)
  1 => 
    array (size=3)
      0 => string '6' (length=1)
      1 => string '7' (length=1)
      2 => string '8' (length=1)

Another option that doesn't have the loop would be:

preg_match_all("#r(\d{1,2})-(\d{1,2})s(\d{1,2})#", $str, $matches, PREG_SET_ORDER);

print_r($matches);

Outputs:

array (size=2)
  0 => 
    array (size=4)
      0 => string 'r4-10s5' (length=7)
      1 => string '4' (length=1)
      2 => string '10' (length=2)
      3 => string '5' (length=1)
  1 => 
    array (size=4)
      0 => string 'r6-7s8' (length=6)
      1 => string '6' (length=1)
      2 => string '7' (length=1)
      3 => string '8' (length=1)

Upvotes: 1

CSᵠ
CSᵠ

Reputation: 10169

The static pattern is: r-s and the empty spots can be only numbers

/r(\d{1,2})-(\d{1,2})s(\d{1,2})/ => test here: http://regex101.com/r/sH4sY0

\d{1,2} matches 1 or 2 digits in a row

Upvotes: 1

Related Questions