Soumalyo Bardhan
Soumalyo Bardhan

Reputation: 166

How to expand a string with PHP, using the FROM and TO numbers specified within square brackets?

Let’s say I have the string http://www.example.com/images/[1-12].jpg. I would like to expand it into:

http://www.example.com/images/1.jpg
http://www.example.com/images/2.jpg
http://www.example.com/images/3.jpg
http://www.example.com/images/4.jpg
http://www.example.com/images/5.jpg
http://www.example.com/images/6.jpg
http://www.example.com/images/7.jpg
http://www.example.com/images/8.jpg
http://www.example.com/images/9.jpg
http://www.example.com/images/10.jpg
http://www.example.com/images/11.jpg
http://www.example.com/images/12.jpg

Here is my code:

$str = "http://www.example.com/images/[1-12].jpg";
while(preg_match_all("/^([^\\[\\]]*)\\[(\\d+)\\-(\\d+)\\](.*)$/m", $str, $mat)){
$arr = array();
$num = sizeof($mat[0]);
    for($i = 0; $i < $num; $i++){
        for($j = $mat[2][$i]; $j <= $mat[3][$i]; $j++){
        $arr[] = rtrim($mat[1][$i].$j.$mat[4][$i]);
        }
    }
$str = implode(PHP_EOL, $arr);
}

It works fine even if I change $str to a more complex expression like the following:

$str = "http://www.example.com/images/[1-4]/[5-8]/[9-14].jpg";

But, unfortunately, zero-padded integers are not supported. So, if I begin with:

$str = "http://www.example.com/images/[001-004].jpg";

Expected result:

http://www.example.com/images/001.jpg
http://www.example.com/images/002.jpg
http://www.example.com/images/003.jpg
http://www.example.com/images/004.jpg

And the actual result:

http://www.example.com/images/001.jpg
http://www.example.com/images/2.jpg
http://www.example.com/images/3.jpg
http://www.example.com/images/4.jpg

How to change this behaviour so that my code produces the expected result? Also, what are the other shortcomings of my code? Should I really do it with the while loop and preg_match_all or are there faster alternatives?

UPDATE: Changing the seventh line of my code into

$arr[] = rtrim($mat[1][$i].str_pad($j, strlen($mat[2][$i]), 0, STR_PAD_LEFT).$mat[4][$i]);

seems to do the trick. Thanks a lot to sjagr for suggesting this. My question is still open because I would like to know the shortcomings of my code and faster alternatives (if any).

Upvotes: 4

Views: 140

Answers (2)

user2680766
user2680766

Reputation:

A faster alternative with preg_split which also supports descending order (FROM > TO):

$str = "http://www.example.com/images/[12-01].jpg";
$spl = preg_split("/\\[([0-9]+)-([0-9]+)\\]/", $str, -1, 2);
$size = sizeof($spl);
$arr = array("");
for($i = 0; $i < $size; $i++){
$temp = array();
    if($i%3 == 1){
    $range = range($spl[$i], $spl[$i+1]);
    $len = min(strlen($spl[$i]), strlen($spl[++$i]));
        foreach($arr as $val){
            foreach($range as $ran){
            $temp[] = $val.str_pad($ran, $len, 0, 0);
            }
        }
    }
    else{
        foreach($arr as $val){
        $temp[] = $val.$spl[$i];
        }
    }
$arr = $temp;
}
$str = implode(PHP_EOL, $arr);
print($str);

It has the following result:

http://www.example.com/images/12.jpg
http://www.example.com/images/11.jpg
http://www.example.com/images/10.jpg
http://www.example.com/images/09.jpg
http://www.example.com/images/08.jpg
http://www.example.com/images/07.jpg
http://www.example.com/images/06.jpg
http://www.example.com/images/05.jpg
http://www.example.com/images/04.jpg
http://www.example.com/images/03.jpg
http://www.example.com/images/02.jpg
http://www.example.com/images/01.jpg

Upvotes: 1

sjagr
sjagr

Reputation: 16502

Per @SoumalyoBardhan's request, my suggestion/answer:

If you use str_pad(), you can force the padding based on the size of the matched string using strlen() found by your match. Your usage of it looks good to me:

$arr[] = rtrim($mat[1][$i].str_pad($j, strlen($mat[2][$i]), 0, STR_PAD_LEFT).$mat[4][$i]);

I really can't see what else could be improved with your code, although I don't exactly understand how preg_match_all would be used in a while statement.

Upvotes: 2

Related Questions