最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin3浏览0评论

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
  • 1 There is a typo. It must be implode(" ", str_split($string, 2)); – Wiimm Commented Mar 16 at 20:05
  • One thing is that to look at how a human would approach a problem is not usually as clear cut as you think it is. People will approach it in various ways. Something which does help though is having a simple and clear solution which makes it easier to maintain. – Nigel Ren Commented Mar 17 at 8:47
  • What in case the three chars not appear as last? Are you certain that never happens? For example 112233344 – Piemol Commented Mar 17 at 9:20
  • 2 @Piemol My examples where chosen to better visualize the chunks. In the real data the chunks may or may not have pairs of digits. One real number from my data is 4292031, which should become 42 92 031, another is 999339, which should become 99 93 39. – Ben Commented Mar 17 at 10:03
  • 1 Here one more regex idea: trim(preg_replace('~...$|..~', '$0 ', $s)) but that's using an additional trim() which might be unwanted. – bobble bubble Commented Mar 18 at 23:23
 |  Show 1 more comment

6 Answers 6

Reset to default 4

You 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 )

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论