Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split a string into chunks of 2, but make the last chunk 3 if it would be 1

Tags:

php

split

I have a series of strings of numbers of different lengths that I need to split into chunks of 2 characters. The last chunk should be 3 characters long, if it would be 1 character long.

For example, 22334455 needs to become 22 33 44 55, but 2233444 needs to become 22 33 444.

I have:

implode(" ", str_split($string, 2));

but that returns 22 33 44 4.

What can I do?

I have thought of:

  • check the length of the array that str_split returns
  • check the length of the last item in the array
  • combine it with the next to last item, if it is of length 1, and drop the last item
  • then implode

but that seems extremely complicated for something that a human would approach like this:

  • if the string is longer than three characters, split the first two characters off
  • if the remainder of the string is longer than three characters, split the next two characters off
  • repeat until the string is three characters long or shorter

That's a recursive function and in PHP might perhaps look something like:

$chunks = array();
while(strlen($string) > 3) {
    $chunks[] = substr($string, 0, 2);
    $string = substr($string, 2);
}
$chunks[] = $string;
implode(" ", $chunks);

But that still seems overly complicated compared to a human who would just evaluate, cut, evaluate, cut, evaluate, not cut.

Is there a more straightforward, less roundabout way?

like image 544
Ben Avatar asked Oct 20 '25 19:10

Ben


2 Answers

You might use a regex to split on:

\S\S\K(?=\S\S)

The regex matches:

  • \S\S Match 2 non whitespace characters
  • \K Forget what is matched so far (this will result in the position to split on)
  • (?=\S\S) Assert that there are 2 non whitespace characters to the right

If you only want to match digits, you can use \d instead of \S

See the matches on regex 101 and a PHP demo

$data = [
    "",
    "2",
    "22",
    "223",
    "2233",
    "22334",
    "223344",
    "2233444",
    "22334455"
];

$pattern = '/\S\S\K(?=\S\S)/';

foreach ($data as $item) {
    var_export(preg_split($pattern, $item));
}

Output

array (
  0 => '',
)array (
  0 => '2',
)array (
  0 => '22',
)array (
  0 => '223',
)array (
  0 => '22',
  1 => '33',
)array (
  0 => '22',
  1 => '334',
)array (
  0 => '22',
  1 => '33',
  2 => '44',
)array (
  0 => '22',
  1 => '33',
  2 => '444',
)array (
  0 => '22',
  1 => '33',
  2 => '44',
  3 => '55',
)
like image 161
The fourth bird Avatar answered Oct 22 '25 10:10

The fourth bird


Wanting to split the string is an XY Problem in the first place. You don't need a temporary array. Just inject the spaces where they are needed -- only at the zero-width position between two 2-digit substrings. Match 2 digits, forget that match (holding a zero-width position in the string), lookahead for 2 more digits. Demo

echo preg_replace(
         '/\d{2}\K(?=\d{2})/',
         ' ',
         $string
     );

/(\d{2})\K(?=(?1))/ and
/\d\d\K(?=\d\d)/ also work the same.

You can even skip-fail the last 2-3 digits in the string then inject the spaces after all other pairs of digits. Demo

echo preg_replace(
         '/\d{2,3}$(*SKIP)(*FAIL)|\d{2}\K/',
         ' ',
         $tests
     );

If you don't like \K in the pattern, a backreference in the replacement will be necessary.

echo preg_replace('/\d\d(?=\d\d)/', '$0 ', $string);`
like image 29
mickmackusa Avatar answered Oct 22 '25 09:10

mickmackusa



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!