Fluffy
Fluffy

Reputation: 28362

How to increment an alphanumeric string in PHP?

There's a string, containing characters [a-zA-Z0-9]. This should be 26 * 2 + 10 = 62 possibilities in one character and 62^2 in two. What is the preferred way to increment a value of such a string, so that 'aA', becomes 'aB', etc.? Is there any built-in stuff in PHP, that can help?

I know you can increment a string, but that's only lowercase letters. Essentially, the result should go from 'a' to 'aa' in 61 increments.

Upvotes: 13

Views: 11402

Answers (7)

You Old Fool
You Old Fool

Reputation: 22941

This is my approach. We iterate backward from the end of the string and increment each character based on $characters. There's no need to split or implode since each character position can be referenced directly by it's index and this is very fast.

This function will loop based on the string length, so if we pass ZZ it will then reset back to 00. If instead you want to continue adding digits so that ZZ become 100 you can set $loop to false:

function increment($x, $loop = true) {
    $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $char_count = strlen($chars);
    $c = strlen($x);
    for ($i = $c - 1; $i >= 0; --$i) {
        $pos = strpos($chars, $x[$i]) + 1;
        $pos = $pos < $char_count ? $pos : 0;
        $x[$i] = $chars[$pos];
        if ($pos > 0) break;
        if (!$loop && !$i && !$pos) $x = $chars[1] . $x;
    }
    return $x;
}

Upvotes: 0

Leonard Korir
Leonard Korir

Reputation: 9

I came to this question as I was looking also for a way to generate auto incrementing alphanumeric unique ID very similar to what we have in mobile money(M-PESA) in Kenya. For reference, here is a screenshot showing the transactions Screenshot showing sample MPESA transactions I'd like to leave it here if anyone is looking also for similar properties: i.e. - Autoincrement alphanumeric - All characters in uppercase - automatic increase of string length once exhausted. - the increment is from 0 to 9 then A to Z before incrementing the adjacent character of number and is a modification of @phobia82's answer

function increment($string){
    $last_char=substr($string,-1);
    $rest=substr($string, 0, -1);
    switch ($last_char) {
        case '':
            $next= 'A';
            break;
        case 'Z':
            $next = '0';
            $unique = increment($rest);
            $rest = $unique;
            break;
        case '9':
            $next= 'A';
            break;
        default:
            $next = ++$last_char;
            break;
    }
    $string=$rest.$next;
    return $string;
}

Upvotes: 1

Bruno Foggia
Bruno Foggia

Reputation: 29

I like this one. How to use: ClassX::increment('p04E7K', 6); ClassX::decrement('p04E7K', 6);

Code:

class ClassX {
    public static $baseCharacters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    public static function increment($x, $digits)
    {
        $charList = static::$baseCharacters;

        // completa cadeia de caracteres com o numero de digitos parametrizados
        $x = trim($x);
        if(strlen($x) < $digits) {
            $x = str_pad($x, $digits, substr($charList, 0, 1), STR_PAD_LEFT);
        }

        $result = preg_split("//", $x, -1, PREG_SPLIT_NO_EMPTY);
        // percorre a cadeia de caracteres de tras pra frente e incrementa o primeiro caractere possivel
        for ($i = $digits - 1; $i >= 0; --$i)
        {
            $char = $result[$i];
            $currentChar = strpos($charList, $char);
            $nextCharPosition = $currentChar+1;

            if($nextCharPosition < strlen($charList)) {
                $nextChar = substr($charList, $nextCharPosition, 1);

                $result[$i] = $nextChar;
                break;
            }
        }

        return implode('', $result);
    }

    public static function decrement($x, $digits)
    {
        $charList = static::$baseCharacters;

        // completa cadeia de caracteres com o numero de digitos parametrizados
        if(strlen($x) < $digits) {
            $x = str_pad($x, $digits, substr($charList, 0, 1), STR_PAD_LEFT);
        }

        $result = preg_split("//", $x, -1, PREG_SPLIT_NO_EMPTY);
        // percorre a cadeia de caracteres de tras pra frente e decrementa o primeiro caractere possivel
        for ($i = $digits - 1; $i >= 0; --$i)
        {
            $char = $result[$i];
            $currentChar = strpos($charList, $char);
            $previousCharPosition = $currentChar-1;

            if($previousCharPosition > -1) {
                $previousChar = substr($charList, $previousCharPosition, 1);

                $result[$i] = $previousChar;

                // define os caracteres apos o decrementado para o ultimo caractere. ex: 3[00] > 2[99]
                for ($j = $i + 1; $j < $digits && $i < $digits - 1; ++$j)
                    $result[$j] = substr($charList, strlen($charList)-1, 1);
                break;
            }
        }

        return implode('', $result);
    }
}

Upvotes: 1

Ivan Buttinoni
Ivan Buttinoni

Reputation: 4145

The @simshaun dont work for me. I check the documentation and I found base_convert that can work for you (on base 35) and a comment of "francesco[at]paladinux.net" with a function to work on base65.

so the solution can be:

convert to b10 -> increment +1 -> convert base 65

EDIT 1

talking about convert I think to base64 coding so I wrote this 2 funcs using base64 encode/decode numbers. Unfortunately the charset used is a bit larger: [a-zA-Z0-9/+=], but using an internal function more efficent.

The Zero 0 is "AA=="

function nencode($in){
        $res ="";
        while(1){
                $b = $in & 0xff;
                $res = chr($b) . $res;

                if($in > 0xff){
                        $in = $in >> 8;
                } else {
                        break;
                }
        }
        return base64_encode($res);
}

function ndecode($in){

        $rv=0;
        $res =base64_decode($in);
        for($t = 0 ; $t < strlen($res) ; $t++){
                if($t>0){
                        $rv = $rv << 8;
                }
                $c = ord($res{$t});
                $rv |= $c;
        }
        return $rv;
}

Upvotes: 3

phobia82
phobia82

Reputation: 1257

Try this function:

<?php
function increment(&$string){
    $last_char=substr($string,-1);
    $rest=substr($string, 0, -1);
    switch ($last_char) {
    case '':
        $next= 'a';
        break;
    case 'z':
        $next= 'A';
        break;
    case 'Z':
        $next= '0';
        break;
    case '9':
        increment($rest);
        $next= 'a';
        break;
    default:
        $next= ++$last_char;
    }
    $string=$rest.$next;
}

    //sample
    $string='a';
    for($i=1;$i<=128;$i++){
        echo $string."<br>";
        increment($string);
    }
    ?>

Upvotes: 6

Daren Chandisingh
Daren Chandisingh

Reputation: 2165

I think that this fundamentally does what you're after.

<?php

$chars = array('', 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');

$increment1 = 0;
$increment2 = 0;

for ($i = 0; $i <= 200; $i++) {
    if ($increment2 === 62) {
        $increment1++;
        $increment2 = 1;
    } else {
        $increment2++;
    }
    echo "{$chars[$increment1]}{$chars[$increment2]} ";
}

/*
Output:
a b c d e f g h i j k l m n o p q r s t u v w x y z 
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 
0 1 2 3 4 5 6 7 8 9 aa ab ac ad ae af ag ah ai aj ak al
am an ao ap aq ar as at au av aw ax ay az aA aB aC aD aE 
aF aG aH aI aJ aK aL aM aN aO aP aQ aR aS aT aU aV aW aX 
aY aZ a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 ba bb bc bd be bf bg 
bh bi bj bk bl bm bn bo bp bq br bs bt bu bv bw bx by bz 
bA bB bC bD bE bF bG bH bI bJ bK bL bM bN bO bP bQ bR bS 
bT bU bV bW bX bY bZ b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ca cb 
cc cd ce cf cg ch ci cj ck cl cm cn co
*/

Upvotes: 1

simshaun
simshaun

Reputation: 21466

This works for me:

<?php
$str = 'a';
echo ++$str; // b

$str = 'z';
echo ++$str; // aa

$str = 'aA';
echo ++$str; // aB

Upvotes: 5

Related Questions