Base36 Encoder/Decoder in C#

Edit: An updated Version (which also adds BigInteger compatibility) is part of my Utility Library on GitHub: https://github.com/mstum/mstum.utils

For a project I am working on I needed a way to convert long numbers into Base 36 numbers. Base 36 uses number 0 to 9 and letters a to z, but is not case sensitive (there is no differentiation between uppercase and lowercase letters), which makes it perfect for being transferred over the telephone.

As the .net Framework does not have a Built-In function for Base 36, I've converted the one from the Wikipedia Article to C#. Note that the "Reverse" function could as well be an extension Method if you use a more recent .net Framework, but to keep it simple it's an instance method here. You can test it against the examples in the Wikipedia article if you want to verify it.

By the way, I've chosen lowercase letters here, but if you want, you can just make them uppercase. Also, note that these functions work with a long, not with a double, so no floating point action here.

// Edit: Slightly updated on 2011-03-29

/// <summary>
/// A Base36 De- and Encoder
/// </summary>
public static class Base36
{
	private const string CharList = "0123456789abcdefghijklmnopqrstuvwxyz";

	/// <summary>
	/// Encode the given number into a Base36 string
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	public static String Encode(long input)
	{
		if (input < 0) throw new ArgumentOutOfRangeException("input", input, "input cannot be negative");

		char[] clistarr = CharList.ToCharArray();
		var result = new Stack<char>();
		while (input != 0)
		{
			result.Push(clistarr[input % 36]);
			input /= 36;
		}
		return new string(result.ToArray());
	}

	/// <summary>
	/// Decode the Base36 Encoded string into a number
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	public static Int64 Decode(string input)
	{
		var reversed = input.ToLower().Reverse();
		long result = 0;
		int pos = 0;
		foreach (char c in reversed)
		{
			result += CharList.IndexOf(c) * (long)Math.Pow(36, pos);
			pos++;
		}
		return result;
	}
}

Comments (17)

nwisoftNovember 6th, 2008 at 11:15

//Thank you for this good sample.
//You just need to Trim start zeros to avoid wrong result : "0H" must return 17.

private const string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

static public long Decode(string value)
{
List database = new List(CHARACTERS);
List tmp = new List(value.ToUpper().TrimStart(new char[] { '0' }).ToCharArray());
tmp.Reverse();

long number = 0;
int index = 0;
foreach (char character in tmp)
{
number += database.IndexOf(character) * (long)Math.Pow(36, index);
index++;
}

return number;
}

static private string Encode(long number)
{
List database = new List(CHARACTERS);
List value = new List();
long tmp = number;

while (tmp != 0)
{
value.Add(database[Convert.ToInt32(tmp % 36)]);
tmp /= 36;
}

value.Reverse();
return new string(value.ToArray());
}

Kofi SarfoMarch 19th, 2009 at 17:20

Thanks so much for having posted this. It saved me however long I'd have been fooling around rolling my own implementation 🙂

Kofi

synhershkoMarch 18th, 2010 at 20:00

Wikipedia had a C# sample, which wasn't working. I updated the code there to use your methods instead, which are working great.

[...] Base36 Encoder/Decoder in C# [...]

Dani RubioMarch 2nd, 2011 at 04:03

@nwisoft
What languaje is that, not csharp guess.. i'm getting mad with your "List" utilization...
I'll better look for the publisher's one...

Jérémie RMarch 29th, 2011 at 17:07

I tried to use this code but it did not work fine.
Try to convert this number : 5481594952936519619
and when I revert I got : 5613318295101298955
as you see there is a problem there

mstumMarch 30th, 2011 at 00:18

@Jérémie That's strange, it works for me on both .net 3.5 and 4.0:

long input = 5481594952936519619L;
var encoded = Base36.Encode(input);
Console.WriteLine(encoded);
var decoded = Base36.Decode(encoded);
Console.WriteLine(decoded);
Console.WriteLine(input == decoded);

Output is:
15n9z8l3au4eb
5481594952936519619
True

How are you using it? I have tried both the above code and the slightly revised version on GitHub with the same result.

Jeff OJuly 17th, 2011 at 12:31

@Jérémie:

Is there any (even slight) chance that this line on your computer may have some localized characters like those that appear in your name? 😉

private const string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

DougJuly 22nd, 2011 at 04:05

Math.Pow(36, index) returns a double that may cause errors at very large numbers when its casted to the long.

mstumJuly 22nd, 2011 at 04:23

That's indeed true, input should have 12 characters or less. This is more intended for database IDs that should turn into URLs. If you are on .net 4, this method can be made a lot more robust through the use of System.Numerics.BigInteger.

kkNovember 3rd, 2011 at 21:41

nwisoft's syntax works fine and is C#, the problem is when he pasted it in the syntax got changed.
the syntax for List should be List followed by less then followed by "char" followed by greater then. If you change the syntax it works fine.

Mika TähtinenNovember 25th, 2011 at 13:51

I ran into some problems converting this code to Visual Basic with .NET Framework 3.5. It seems that division with "/" uses double precision. The solution is to force integer division using "\=" instead of "/=".

mstumNovember 26th, 2011 at 00:54

Yes, you will need to ensure Integer Division. In C#, the / operator is overloaded and performs integer division if both operands are integers, otherwise it does double division.

mstumNovember 26th, 2011 at 00:56

I've just updated the Library on GitHub to add a BigInteger Version. This should ensure compatibility with large numbers in situations where a long overflows.

StefanFebruary 10th, 2012 at 09:27

Very grateful for this post, saved me a lot of time I didn't have! 😀

MaxAugust 2nd, 2012 at 18:33

Another thank you!!!

Nicholas NemtsevDecember 14th, 2012 at 11:08

You forgot about the KISS (http://en.wikipedia.org/wiki/KISS_principle) principle:

static public long Decode2(string value)
{
long number = 0;
foreach(char c in value)
number = number * 36 + CHARACTERS.IndexOf(c);

return number;
}