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?
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?
Share Improve this question edited Mar 17 at 8:56 Ben asked Mar 16 at 19:38 BenBen 74114 bronze badges 6 | Show 1 more comment6 Answers
Reset to default 4You might use a regex to split on:
\S\S\K(?=\S\S)
The regex matches:
\S\S
Match 2 non whitespace characters\K
Fet 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',
)
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, fet 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);`
You can do a little postprocessing: if the string ends in a space and a single number, then remove the space. A simple regular expression can do this. If the pattern in the expression doesn't match because there is a space and two numbers then nothing happens.
The expression is preg_replace('/ (\d)$/', '\1', $imploded)
, with the pattern / (\d)$/
which means 'a space, then a match group with one digit \d
, then the end of the string $
', and \1
means 'replace the entire pattern with the contents of the first match group', which in this case is the digit that was matched. So the space is matched but not replaced, therefore the space is effectively removed if the pattern matches.
Example in the terminal:
$ php -a
php > $mystring = "2233444";
php > $imploded = implode(" ", str_split($mystring, 2));
php > $combined = preg_replace('/ (\d)$/', '\1', $imploded);
php > echo $combined;
22 33 444
php > $mystring = "22334455";
php > $imploded = implode(" ", str_split($mystring, 2));
php > $combined = preg_replace('/ (\d)$/', '\1', $imploded);
php > echo $combined;
22 33 44 55
Here is a solution based on preg_replace()
. The number or replacements is set by the $limit
parameter:
function process(string $str): string
{
if(strlen($str) > 3)
$str = preg_replace('/(..)/', '\\1 ', $str, intdiv(strlen($str) - 2, 2));
return $str;
}
echo process('22334455'), PHP_EOL;
echo process('2233444'), PHP_EOL;
Output:
22 33 44 55
22 33 444
(demo)
++ You can use this too
function splitChunks($string) {
return implode(' ', strlen($string) % 2
? array_merge(str_split(substr($string, 0, -3), 2), [substr($string, -3)])
: str_split($string, 2)
);
}
echo splitChunks('2233444'), PHP_EOL;
Split by 2, if the input is an odd length, merge the last two chunks.
function toChunks($input)
{
$result = str_split($input, 2);
if(strlen($input) % 2 === 1){
$length = sizeof($result);
if($length > 1){
$result[$length - 2] .= $result[$length - 1]; // add last to previous
unset($result[$length - 1]);
}
}
return $result;
}
echo '<br><br>';
echo 'toChunks()';
echo '<br>';
foreach(array('112233', '1122333', '11223333', '1122233', '123', '12345', '1234567') as $string){
echo 'Input: '.$string;
echo '<br>';
echo 'Output: '.print_r(toChunks($string), true);
echo '<br><br>';
}
Produces:
toChunks()
Input: 112233
Output: Array ( [0] => 11 [1] => 22 [2] => 33 )
Input: 1122333
Output: Array ( [0] => 11 [1] => 22 [2] => 333 )
Input: 11223333
Output: Array ( [0] => 11 [1] => 22 [2] => 33 [3] => 33 )
Input: 1122233
Output: Array ( [0] => 11 [1] => 22 [2] => 233 )
Input: 123
Output: Array ( [0] => 123 )
Input: 12345
Output: Array ( [0] => 12 [1] => 345 )
Input: 1234567
Output: Array ( [0] => 12 [1] => 34 [2] => 567 )
implode(" ", str_split($string, 2));
– Wiimm Commented Mar 16 at 20:05112233344
– Piemol Commented Mar 17 at 9:204292031
, which should become42 92 031
, another is999339
, which should become99 93 39
. – Ben Commented Mar 17 at 10:03trim(preg_replace('~...$|..~', '$0 ', $s))
but that's using an additionaltrim()
which might be unwanted. – bobble bubble Commented Mar 18 at 23:23