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; } }
//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());
}
Thanks so much for having posted this. It saved me however long I'd have been fooling around rolling my own implementation 🙂
Kofi
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# [...]
@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...
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
@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.
@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";
Math.Pow(36, index) returns a double that may cause errors at very large numbers when its casted to the long.
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.
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.
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 "/=".
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.
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.
Very grateful for this post, saved me a lot of time I didn't have! 😀
Another thank you!!!
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;
}