I have an array of bytes (any length), and I want to encode this array into string using my own base encoder. In .NET
is standard Base64
encoder, but what if I want to encode the array in Base62
, Base53
or Base13
?
Is it even possible to create such universal base encoder?
I know I could do it the simple way, that is, for each byte reserve fixed number of chars (in case of Base62
, that would be 5 chars), and do direct byte->chars encoding, but I would be wasting space, as 5 Base62
chars are able to contain more than 1 byte, but less than 2 bytes.
How should I write such an encoder? Or is there already some class for this?
And please note that I need universal decoder as well, otherwise this is useless to me.
As the solution is already known (use BigInteger
), I would just like to put here some resources relating the BigInteger
class, as it is not available in .NET 3.5:
Big integers in C#
http://intx.codeplex.com/
https://svn.apache.org/repos/asf/incubator/heraldry/libraries/csharp/openid/trunk/Mono/Mono.Math/BigInteger.cs
http://www.codeproject.com/KB/cs/BigInteger_Library.aspx
http://www.codeproject.com/KB/cs/biginteger.aspx
The BigInteger class has a longValue() method to convert a byte array to a long value: long value = new BigInteger(bytes). longValue();
To convert byte array to a hex value, we loop through each byte in the array and use String 's format() . We use %02X to print two places ( 02 ) of Hexadecimal ( X ) value and store it in the string st .
One method is to create a string variable and then append the byte value to the string variable with the help of + operator. This will directly convert the byte value to a string and add it in the string variable. The simplest way to do so is using valueOf() method of String class in java.
The absolute safest way to convert bytes to a string and back is to use base64: string base64 = Convert. ToBase64String(bytes); byte[] bytes = Convert. FromBase64String(base64);
A little late to the party, but...
Because your specification calls for an arbitrary number of bits, you must have an integer type that can work with an arbitrary number of bits. If you can't target .NET 4.0 you'll have to beg, borrow, or steal a BigInteger implementation somewhere (like .NET 4.0 perhaps).
public static class GenericBaseConverter
{
public static string ConvertToString(byte[] valueAsArray, string digits, int pad)
{
if (digits == null)
throw new ArgumentNullException("digits");
if (digits.Length < 2)
throw new ArgumentOutOfRangeException("digits", "Expected string with at least two digits");
BigInteger value = new BigInteger(valueAsArray);
bool isNeg = value < 0;
value = isNeg ? -value : value;
StringBuilder sb = new StringBuilder(pad + (isNeg ? 1 : 0));
do
{
BigInteger rem;
value = BigInteger.DivRem(value, digits.Length, out rem);
sb.Append(digits[(int)rem]);
} while (value > 0);
// pad it
if (sb.Length < pad)
sb.Append(digits[0], pad - sb.Length);
// if the number is negative, add the sign.
if (isNeg)
sb.Append('-');
// reverse it
for (int i = 0, j = sb.Length - 1; i < j; i++, j--)
{
char t = sb[i];
sb[i] = sb[j];
sb[j] = t;
}
return sb.ToString();
}
public static BigInteger ConvertFromString(string s, string digits)
{
BigInteger result;
switch (Parse(s, digits, out result))
{
case ParseCode.FormatError:
throw new FormatException("Input string was not in the correct format.");
case ParseCode.NullString:
throw new ArgumentNullException("s");
case ParseCode.NullDigits:
throw new ArgumentNullException("digits");
case ParseCode.InsufficientDigits:
throw new ArgumentOutOfRangeException("digits", "Expected string with at least two digits");
case ParseCode.Overflow:
throw new OverflowException();
}
return result;
}
public static bool TryConvertFromString(string s, string digits, out BigInteger result)
{
return Parse(s, digits, out result) == ParseCode.Success;
}
private enum ParseCode
{
Success,
NullString,
NullDigits,
InsufficientDigits,
Overflow,
FormatError,
}
private static ParseCode Parse(string s, string digits, out BigInteger result)
{
result = 0;
if (s == null)
return ParseCode.NullString;
if (digits == null)
return ParseCode.NullDigits;
if (digits.Length < 2)
return ParseCode.InsufficientDigits;
// skip leading white space
int i = 0;
while (i < s.Length && Char.IsWhiteSpace(s[i]))
++i;
if (i >= s.Length)
return ParseCode.FormatError;
// get the sign if it's there.
BigInteger sign = 1;
if (s[i] == '+')
++i;
else if (s[i] == '-')
{
++i;
sign = -1;
}
// Make sure there's at least one digit
if (i >= s.Length)
return ParseCode.FormatError;
// Parse the digits.
while (i < s.Length)
{
int n = digits.IndexOf(s[i]);
if (n < 0)
return ParseCode.FormatError;
BigInteger oldResult = result;
result = unchecked((result * digits.Length) + n);
if (result < oldResult)
return ParseCode.Overflow;
++i;
}
// skip trailing white space
while (i < s.Length && Char.IsWhiteSpace(s[i]))
++i;
// and make sure there's nothing else.
if (i < s.Length)
return ParseCode.FormatError;
if (sign < 0)
result = -result;
return ParseCode.Success;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With