Split a string AFTER every third instance of character
Asked Answered
B

10

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.

Beverage answered 13/8, 2009 at 22:30 Comment(0)
L
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;
}
Lightheaded answered 13/8, 2009 at 22:37 Comment(0)
T
6

Easiest solution I can think of is:

$chunks = array_chunk(explode(';', $input), 3);
$output = array_map(create_function('$a', 'return implode(";",$a);'), $chunks);
Tibold answered 13/8, 2009 at 23:52 Comment(2)
+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);Supernormal
Yeah true. I guess we can start using 5.3 features in answers now it's released.Tibold
S
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));
};
Supernormal answered 13/8, 2009 at 23:50 Comment(0)
T
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; )

Tangential answered 13/8, 2009 at 22:39 Comment(2)
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.Donyadoodad
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;;;']Pluvious
T
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;
        )

)
Tangential answered 13/8, 2009 at 22:56 Comment(1)
This fails when the number of pieces isn't a multiple of 3.Supernormal
S
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;
)
Stutman answered 13/8, 2009 at 22:58 Comment(0)
M
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
        )

)
Mcconaghy answered 13/8, 2009 at 23:2 Comment(1)
to format the output array as described: $m = array_map(create_function('$a', 'return $a[0];'), $m);Agonized
H
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"
}
Heal answered 13/8, 2009 at 23:7 Comment(0)
B
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;"
}
Betoken answered 15/11, 2020 at 19:13 Comment(0)
P
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.

Pluvious answered 8/9, 2022 at 1:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.