5

How can I explode a string at the position after every third semicolon (;)?

example data:

$string = 'piece1;piece2;piece3;piece4;piece5;piece6;piece7;piece8;';

Desired output:

$output[0] = 'piece1;piece2:piece3;'

$output[1] = 'piece4;piece5;piece6;'

$output[2] = 'piece7;piece8;'

Notice the trailing semicolons are retained.

0

10 Answers 10

8

I am sure you can do something slick with regular expressions, but why not just explode the each semicolor and then add them three at a time.

$tmp = explode(";", $string);
$i=0;
$j=0;

foreach($tmp as $piece) {
   if(! ($i++ %3)) $j++;   //increment every 3 
   $result[$j] .= $piece;
}
Sign up to request clarification or add additional context in comments.

Comments

6

Easiest solution I can think of is:

$chunks = array_chunk(explode(';', $input), 3);
$output = array_map(create_function('$a', 'return implode(";",$a);'), $chunks);

2 Comments

+1 for the first line, -1 for the second ;) (using create_function() is bad practive). Starting from PHP 5.3 you can use a lambda function for it, though: $output = array_map(function($a) { return implode(';', $a); }, $chunks);
Yeah true. I guess we can start using 5.3 features in answers now it's released.
3

Essentially the same solution as the other ones that explode and join again...

$tmp = explode(";", $string);

while ($tmp) {
    $output[] = implode(';', array_splice($tmp, 0, 3));
};

Comments

1
$string = "piece1;piece2;piece3;piece4;piece5;piece6;piece7;piece8;piece9;";
preg_match_all('/([A-Za-z0-9\.]*;[A-Za-z0-9\.]*;[A-Za-z0-9\.]*;)/',$string,$matches);

print_r($matches);

Array
(
    [0] => Array
        (
            [0] => piece1;piece2;piece3;
            [1] => piece4;piece5;piece6;
            [2] => piece7;piece8;piece9;
        )

    [1] => Array
        (
            [0] => piece1;piece2;piece3;
            [1] => piece4;piece5;piece6;
            [2] => piece7;piece8;piece9;
        )

)

1 Comment

This fails when the number of pieces isn't a multiple of 3.
1

Maybe approach it from a different angle. Explode() it all, then combine it back in triples. Like so...

$str = "1;2;3;4;5;6;7;8;9";
$boobies = explode(";", $array);
while (!empty($boobies))
{
  $foo = array();
  $foo[] = array_shift($boobies);
  $foo[] = array_shift($boobies);
  $foo[] = array_shift($boobies);
  $bar[] = implode(";", $foo) . ";";
}

print_r($bar);

Array ( [0] => 1;2;3; [1] => 4;5;6; [2] => 7;8;9; )

2 Comments

Downvote removed, but I have to say I find while (!empty($boobies)) rather disturbing. And I hope you don't pornify your variable names at work.
Not only is there a typo where $str is never used, this answer is only good when the number of delimited values is divisible by 3. Proof When 7 values it returns `['1;2;3;', '4;5;6;', '7;;;']
1

Here's a regex approach, which I can't say is all too good looking.

$str='';
for ($i=1; $i<20; $i++) {
    $str .= "$i;";
}

$split = preg_split('/((?:[^;]*;){3})/', $str, -1,
                    PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

Output:

Array
(
    [0] => 1;2;3;
    [1] => 4;5;6;
    [2] => 7;8;9;
    [3] => 10;11;12;
    [4] => 13;14;15;
    [5] => 16;17;18;
    [6] => 19;
)

Comments

1

Another regex approach.

<?php
$string = 'piece1;piece2;piece3;piece4;piece5;piece6;piece7;piece8';
preg_match_all('/([^;]+;?){1,3}/', $string, $m, PREG_SET_ORDER);
print_r($m);

Results:

Array
(
    [0] => Array
        (
            [0] => piece1;piece2;piece3;
            [1] => piece3;
        )

    [1] => Array
        (
            [0] => piece4;piece5;piece6;
            [1] => piece6;
        )

    [2] => Array
        (
            [0] => piece7;piece8
            [1] => piece8
        )

)

1 Comment

to format the output array as described: $m = array_map(create_function('$a', 'return $a[0];'), $m);
0

Regex Split

$test = ";2;3;4;5;6;7;8;9;10;;12;;14;15;16;17;18;19;20";
// match all groups that:
// (?<=^|;) follow the beginning of the string or a ;
// [^;]*  have zero or more non ; characters
// ;? maybe a semi-colon (so we catch a single group)
// [^;]*;? again (catch second item)
// [^;]* without the trailing ; (to not capture the final ;)
preg_match_all("/(?<=^|;)[^;]*;?[^;]*;?[^;]*/", $test, $matches);
var_dump($matches[0]);


array(7) {
  [0]=>
  string(4) ";2;3"
  [1]=>
  string(5) "4;5;6"
  [2]=>
  string(5) "7;8;9"
  [3]=>
  string(6) "10;;12"
  [4]=>
  string(6) ";14;15"
  [5]=>
  string(8) "16;17;18"
  [6]=>
  string(5) "19;20"
}

Comments

0
<?php
$str = 'piece1;piece2;piece3;piece4;piece5;piece6;piece7;piece8;';
$arr = array_map(function ($arr) {
    return implode(";", $arr);
}, array_chunk(explode(";", $str), 3));
var_dump($arr);

outputs

 array(3) {
  [0]=>
  string(20) "piece1;piece2;piece3"
  [1]=>
  string(20) "piece4;piece5;piece6"
  [2]=>
  string(14) "piece7;piece8;"
}

Comments

0

Similar to @Sebastian's earlier answer, I recommend preg_split() with a repeated pattern. The difference is that by using a non-capturing group and appending \K to restart the fullstring match, you can spare writing the PREG_SPLIT_DELIM_CAPTURE flag.

Code: (Demo)

$string = 'piece1;piece2;piece3;piece4;piece5;piece6;piece7;piece8;';

var_export(preg_split('/(?:[^;]*;){3}\K/', $string, 0, PREG_SPLIT_NO_EMPTY));

A similar technique for splitting after every 2 things can be found here. That snippet actually writes the \K before the last space character so that the trailing space is consumed while splitting.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.