3

I need to convert an integer into a base64-character representation. I'm using OxA3's answer on this thread: Quickest way to convert a base 10 number to any base in .NET?

How do I inverse this to get my original integer back, given a string?

Community
  • 1
  • 1
ashes999
  • 9,925
  • 16
  • 73
  • 124

5 Answers5

4

Joel Mueller's answer should guide you the base-64 case.

In response to the preliminary code you've provided in your own answer, you can definitely improve its efficiency by changing the code to accomplish what your for loop is doing (effectively an O(N) IndexOf) to use a hash lookup (which should make it O(1)).

I am basing this on the assumption that baseChars is a field that you initialize in your class's constructor. If this is correct, make the following adjustment:

private Dictionary<char, int> baseChars;

// I don't know what your class is called.
public MultipleBaseNumberFormatter(IEnumerable<char> baseCharacters)
{
    // check for baseCharacters != null and Count > 0

    baseChars = baseCharacters
        .Select((c, i) => new { Value = c, Index = i })
        .ToDictionary(x => x.Value, x => x.Index);
}

Then in your StringToInt method:

char next = encodedString[currentChar];

// No enumerating -- we've gone from O(N) to O(1)!
if (!characterIndices.TryGetValue(next, out nextCharIndex))
{
    throw new ArgumentException("Input includes illegal characters.");
}
Community
  • 1
  • 1
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
3

I have a first-pass of a working version here, albeit I'm not sure how efficient it is.

public static int StringToInt(string encodedString, char[] baseChars)
    {
        int result = 0;
        int sourceBase = baseChars.Length;
        int nextCharIndex = 0;

        for (int currentChar = encodedString.Length - 1; currentChar >= 0; currentChar--)
        {
            char next = encodedString[currentChar];

            // For loop gets us: baseChar.IndexOf(char) => int
            for (nextCharIndex = 0; nextCharIndex < baseChars.Length; nextCharIndex++)
            {
                if (baseChars[nextCharIndex] == next)
                {
                    break;
                }
            }

            // For character N (from the end of the string), we multiply our value
            // by 64^N. eg. if we have "CE" in hex, F = 16 * 13.
            result += (int)Math.Pow(baseChars.Length, encodedString.Length - 1 - currentChar) * nextCharIndex;
        }

        return result;
    }
jtate
  • 2,612
  • 7
  • 25
  • 35
ashes999
  • 9,925
  • 16
  • 73
  • 124
2

Here is a version using Linq functionality and .NET Framework 4.0 Zip extension to perform the calculation.

public static int StringToInt(string encodedString, char[] baseChars) {
    int sourceBase = baseChars.Length;

    var dict = baseChars
        .Select((c, i) => new { Value = c, Index = i })
        .ToDictionary(x => x.Value, x => x.Index);

    return encodedString.ToCharArray()
        // Get a list of positional weights in descending order, calcuate value of weighted position
        .Zip(Enumerable.Range(0,encodedString.Length).Reverse(), (f,s) => dict[f] * (int)Math.Pow(sourceBase,s)) 
        .Sum();
}

FYI, calculating the dictionary once outside the function would be more efficient for a large batch of transformations.

Norman H
  • 2,248
  • 24
  • 27
1

Here's a complete solution that converts a base10 number to baseK and back:

public class Program
{
    public static void Main()
    {
        int i = 100;

        Console.WriteLine("Int:               " + i);

        // Default base definition. By moving chars around in this string, we can further prevent
        // users from guessing identifiers.
        var baseDefinition = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        //var baseDefinition = "WBUR17GHO8FLZIA059M4TESD2VCNQKXPJ63Y"; // scrambled to minimize guessability

        // Convert base10 to baseK
        var newId = ConvertToBaseK(i, baseDefinition);
        Console.WriteLine(string.Format("To base{0} (short): {1}", baseDefinition.Length, newId));

        // Convert baseK to base10
        var convertedInt2 = ConvertToBase10(newId, baseDefinition);
        Console.WriteLine(string.Format("Converted back:    {0}", convertedInt2));
    }

    public static string ConvertToBaseK(int val, string baseDef)
    {
        string result = string.Empty;
        int targetBase = baseDef.Length;

        do
        {
            result = baseDef[val % targetBase] + result;
            val = val / targetBase;
        } 
        while (val > 0);

        return result;
    }

    public static int ConvertToBase10(string str, string baseDef)
    {
        double result = 0;
        for (int idx = 0; idx < str.Length; idx++)
        {
            var idxOfChar = baseDef.IndexOf(str[idx]);
            result += idxOfChar * System.Math.Pow(baseDef.Length, (str.Length-1) - idx);
        }

        return (int)result;
    }
}
tbehunin
  • 1,043
  • 1
  • 12
  • 24
0

If base-64 is really what you need rather than "any base" then everything you need is already built in to the framework:

int orig = 1337;
byte[] origBytes = BitConverter.GetBytes(orig);
string encoded = Convert.ToBase64String(origBytes);
byte[] decoded = Convert.FromBase64String(encoded);
int converted = BitConverter.ToInt32(decoded, 0);
System.Diagnostics.Debug.Assert(orig == converted);
Joel Mueller
  • 28,324
  • 9
  • 63
  • 88
  • god thats ugly compared to C. Don't know why people rave so much about this C#. – ldog Aug 26 '10 at 23:00
  • 1
    @ldog: Haha, I can't imagine that anybody who's ever raved about C# has picked *converting strings to integers for arbitrary bases* as a scenario in which it really shines. Comparing C# to C in general seems rather pointless to me, in fact. Suppose I complained about how hard it is to rapidly develop a rich GUI application in C and asked why anybody likes it. – Dan Tao Aug 26 '10 at 23:20
  • It doesn't look like it's working. The original function maps (1, 2, 3, 4) to (b, c, d, e); this one maps them to (AqAAAA, AgAAAA, AwAAAA, BAAAAA). Not the original intention, which is to use less characters for numbers. – ashes999 Aug 27 '10 at 16:39
  • You said you wanted base-64. That's standard base-64 encoding on the 4 bytes that make up an integer (that is, 1 = [0x1, 0x0, 0x0, 0x0]). Unfortunately, `Convert.ToBase64String` only accepts byte arrays. – Joel Mueller Aug 27 '10 at 17:46
  • The original Stack Overflow post lays it out as converting to "any base int." That's what I'm after. Cheers though. – ashes999 Aug 27 '10 at 19:33