How to increment letters like numbers in PHP?

Php

Php Problem Overview


I would like to write a function that takes in 3 characters and increments it and returns the newly incremented characters as a string.

I know how to increase a single letter to the next one but how would I know when to increase the second letters and then stop and then increase the first letter again to have a sequential increase?

> So if AAA is passed, return AAB. If > AAZ is passed return ABA (hard part).

I would appreciate help with the logic and what php functions will be useful to use.

Even better, has some done this already or there is a class available to do this??

Thanks all for any help

Php Solutions


Solution 1 - Php

Character/string increment works in PHP (though decrement doesn't)

$x = 'AAZ';
$x++;
echo $x;  // 'ABA'

Solution 2 - Php

You can do it with the ++ operator.

$i = 'aaz';
$i++;
print $i;

aba

However this implementation has some strange things:

for($i = 'a'; $i < 'z'; $i++) print "$i ";

This will print out letters from a to y.

for($i = 'a'; $i <= 'z'; $i++) print "$i ";

This will print out lettes from a to z and it continues with aa and ends with yz.

Solution 3 - Php

As proposed in PHP RFC: Strict operators directive (currently Under Discussion):

> Using the increment function on a string will throw a TypeError when strict_operators is enabled.

Whether or not the RFC gets merged, PHP will sooner or later go that direction of adding operator strictness. Therefore, you should not be incrementing strings.

a-z/A-Z ranges

If you know your letters will stay in range a-z/A-Z (not surpass z/Z), you can use the solution that converts letter to ASCII code, increments it, and converts back to letter.

Use ord() a chr():

$letter = 'A';
$letterAscii = ord($letter);
$letterAscii++;
$letter = chr($letterAscii); // 'B'
  1. ord() converts the letter into ASCII num representation
  2. that num representation is incremented
  3. using chr() the number gets converted back to the letter

As discovered in comments, be careful. This iterates ASCII table so from Z (ASCII 90), it does not go to AA, but to [ (ASCII 91).

Going beyond z/Z

If you dare to go further and want z became aa, this is what I came up with:

final class NextLetter
{
    private const ASCII_UPPER_CASE_BOUNDARIES = [65, 91];
    private const ASCII_LOWER_CASE_BOUNDARIES = [97, 123];

    public static function get(string $previous) : string
    {
        $letters = str_split($previous);
        $output = '';
        $increase = true;

        while (! empty($letters)) {
            $letter = array_pop($letters);

            if ($increase) {
                $letterAscii = ord($letter);
                $letterAscii++;
                if ($letterAscii === self::ASCII_UPPER_CASE_BOUNDARIES[1]) {
                    $letterAscii = self::ASCII_UPPER_CASE_BOUNDARIES[0];
                    $increase = true;
                } elseif ($letterAscii === self::ASCII_LOWER_CASE_BOUNDARIES[1]) {
                    $letterAscii = self::ASCII_LOWER_CASE_BOUNDARIES[0];
                    $increase = true;
                } else {
                    $increase = false;
                }

                $letter = chr($letterAscii);
                if ($increase && empty($letters)) {
                    $letter .= $letter;
                }
            }

            $output = $letter . $output;
        }

        return $output;
    }
}

I'm giving you also 100% coverage if you intend to work with it further. It tests against original string incrementation ++:

    /**
     * @dataProvider letterProvider
     */
    public function testIncrementLetter(string $givenLetter) : void
    {
        $expectedValue = $givenLetter;

        self::assertSame(++$expectedValue, NextLetter::get($givenLetter));
    }

    /** 
     * @return iterable<array<string>>
     */
    public function letterProvider() : iterable
    {
        yield ['A'];
        yield ['a'];
        yield ['z'];
        yield ['Z'];
        yield ['aaz'];
        yield ['aaZ'];
        yield ['abz'];
        yield ['abZ'];
    }

Solution 4 - Php

To increment or decrement in the 7bits 128 chars ASCII range, the safest:

$CHAR = "l";
echo chr(ord($CHAR)+1)." ".chr(ord($CHAR)-1);
/* m k */

So, it is normal to get a backtick by decrementing a, as the ascii spec list

Print the whole ascii range:

for ($i = 0;$i < 127;$i++){
    echo chr($i);
}
/* !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ */

More infos about ANSI 7 bits ASCII: man ascii


To increment or decrement in the 8-bits extended 256 chars UTF-8 range.

This is where it starts to differ regarding the host machine charset. but those charsets are all available on modern machines. From php, the safest is to use the php-mbstring extension: https://www.php.net/manual/en/function.mb-chr.php

> Extended ASCII (EASCII or high ASCII) character encodings are > eight-bit or larger encodings that include the standard seven-bit > ASCII characters, plus additional characters. https://en.wikipedia.org/wiki/Extended_ASCII

More info, as example: man iso_8859-9

   ISO 8859-1    West European languages (Latin-1)
   ISO 8859-2    Central and East European languages (Latin-2)
   ISO 8859-3    Southeast European and miscellaneous languages (Latin-3)
   ISO 8859-4    Scandinavian/Baltic languages (Latin-4)
   ISO 8859-5    Latin/Cyrillic
   ISO 8859-6    Latin/Arabic
   ISO 8859-7    Latin/Greek
   ISO 8859-8    Latin/Hebrew
   ISO 8859-9    Latin-1 modification for Turkish (Latin-5)
   ISO 8859-10   Lappish/Nordic/Eskimo languages (Latin-6)
   ISO 8859-11   Latin/Thai
   ISO 8859-13   Baltic Rim languages (Latin-7)
   ISO 8859-14   Celtic (Latin-8)
   ISO 8859-15   West European languages (Latin-9)
   ISO 8859-16   Romanian (Latin-10)

Example, we can find the symbol in ISO 8859-7:

244   164   A4     €     EURO SIGN

To increment or decrement in the 16 bits UTF-16 Unicode range:

Here is a way to generate the whole unicode charset, by generating html entities and converting to utf8. Run it online

for ($x = 0; $x < 262144; $x++){
  echo html_entity_decode("&#".$x.";",ENT_NOQUOTES,"UTF-8");
}

Same stuff, but the range goes up to (16^4 * 4)!

echo html_entity_decode('&#33;',ENT_NOQUOTES,'UTF-8');
/* ! */
echo html_entity_decode('&#34;',ENT_NOQUOTES,'UTF-8');
/* " */

To retrieve the unicode symbol,using the base10 decimal representation of the character.

echo html_entity_decode('&#8364;',ENT_NOQUOTES,'UTF-8');
/* € */

The same symbol, using the base16 hexadecimal representation:

echo html_entity_decode('&#'.hexdec("20AC").';',ENT_NOQUOTES,'UTF-8');
/* € */

First 32 bits are reserved for special control characters, output garbage �����, but have a meaning.

Solution 5 - Php

You are looking at a number representation problem. This is base24 (or however many numbers your alphabet has). Lets call the base b.

Assign a number to each letter in alphabet (A=1, B=2, C=3).

Next, figure out your input "number": The representation "ABC" means A*b^2 + B*b^1 + C*b^0 Use this formula to find the number (int). Increment it.

Next, convert it back to your number system: Divide by b^2 to get third digit, the remainder (modulo) by b^1 for second digit, the remainder (modulo) by `b^0^ for last digit.

This might help: How to convert from base10 to any other base.

Solution 6 - Php

You could use the ASCII codes for alpha numerics. From there you increment and decrement to get the previous/next character.

You could split your string in single characters and then apply the transformations on these characters.

Just some thoughts to get you started.

Solution 7 - Php

 <?php 
$values[] = 'B';
$values[] = 'A';
$values[] = 'Z';
foreach($values as $value ){
  if($value == 'Z'){ 
       $value = '-1';
    }
$op = ++$value;
echo $op;
}
?>

Solution 8 - Php

I have these methods in c# that you could probably convert to php and modify to suit your needs, I'm not sure Hexavigesimal is the exact name for these though...

#region Hexavigesimal (Excel Column Name to Number)
public static int FromHexavigesimal(this string s)
{
	int i = 0;
	s = s.Reverse();
	for (int p = s.Length - 1; p >= 0; p--)
	{
		char c = s[p];
		i += c.toInt() * (int)Math.Pow(26, p);
	}

	return i;
}

public static string ToHexavigesimal(this int i)
{
	StringBuilder s = new StringBuilder();

	while (i > 26)
	{
		int r = i % 26;
		if (r == 0)
		{
			i -= 26;
			s.Insert(0, 'Z');
		}
		else
		{
			s.Insert(0, r.toChar());
		}

		i = i / 26;
	}

	return s.Insert(0, i.toChar()).ToString();
}

public static string Increment(this string s, int offset)
{
	return (s.FromHexavigesimal() + offset).ToHexavigesimal();
}

private static char toChar(this int i)
{
	return (char)(i + 64);
}

private static int toInt(this char c)
{
	return (int)c - 64;
}
#endregion

EDIT

I see by the other answers that in PHP you can use ++ instead, nice!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAbsView Question on Stackoverflow
Solution 1 - PhpMark BakerView Answer on Stackoverflow
Solution 2 - PhptamasdView Answer on Stackoverflow
Solution 3 - PhpsimPodView Answer on Stackoverflow
Solution 4 - PhpNVRMView Answer on Stackoverflow
Solution 5 - PhpDaren ThomasView Answer on Stackoverflow
Solution 6 - PhpAndreasView Answer on Stackoverflow
Solution 7 - PhpVimal Kumar EView Answer on Stackoverflow
Solution 8 - PhpCaffGeekView Answer on Stackoverflow