将字节数组转换为任何基数

我有一个字节数组(任意长度),我想用我自己的基本编码器将这个数组编码成字符串。 在.NET是标准的Base64编码器,但是如果我想在Base62Base53Base13对数组进行编码呢?

甚至可以创建这样的通用基础编码器?

我知道我可以用简单的方法做到这一点,也就是说,对于每个字节保留固定数量的字符(在Base62情况下,这将是5个字符),并且做直接字节 – >字符编码,但我会浪费空间,因为5个Base62字符能够包含多于1个字节,但少于2个字节。

我该怎么写这样的编码器? 或者已经有一些课程了吗?
请注意我也需要通用解码器,否则这对我来说没用。

资源

由于解决方案已经知道(使用BigInteger ),我想在这里放一些与BigInteger类相关的资源,因为它在.NET 3.5中不可用:

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

如果性能不是问题,请在后台使用BigInteger类。 你有一个BigInteger的构造函数,它接受字节数组,然后你可以手动运行除法和模数的循环来获得其他非标准基数的表示。

另外看看这个 。

派对有点晚了,但……

因为您的规范要求任意数量的位,所以必须具有可以使用任意位数的整数类型。 如果您无法定位.NET 4.0,则必须在某处(例如.NET 4.0)乞讨,借用或窃取BigInteger实现。

 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; } } 

这是我博客的副本,希望能帮助我转换为Base62的方式(以及为什么)

我目前正在开发自己的url缩短程序:konv.es。 为了创建url的最短字符哈希值,我使用字符串的GetHashCode()方法,然后将得到的数字转换为base 62([0-9a-zA-Z])。 到目前为止,我发现最优雅的解决方案是转换(这也是收益率回报的一个方便的例子):

 public static IEnumerable ToBase62(int number) { do { yield return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"[number % 62]; number /= 62; } while (number > 0); } 

额外信用:重新考虑作为扩展方法

您可以从Michael Giagnocavo的Base32实现的C#实现中获得灵感。

BASE64运行良好,因为64是2(2 ^ 6)的幂,所以每个字符保存6位数据,3个字节(3 * 8 = 24位)可以编码为4个字符(4 * 6 = 24)。 编码和解码可以仅仅是位移位。

对于不与2的幂对齐的基数(如基数62​​或基数53),则必须将要尝试编码的消息视为一个长数,并对其执行divison和modulo操作。 您可能最好使用Base32编码并浪费一点带宽。

另一个要查看的示例是Ascii85 ,用于Adobe PostScript和PDF文档。 在Ascii85中,5个字符用于编码4个字节。 您可以将此编码的效率计算为(256 ^ 4)/(85 ^ 5)= 96.8%。 这是实际使用的比特组合的一部分。

因此,对于您希望用于编码数据的任何新基础,如果您正在尝试最大化编码效率,那么您希望寻找能够使其高于256的功率的功率。 对于每个基地来说,这可能并不容易。 检查基数53表明,你可能得到的最好的是使用7个字节来编码5个字节(效率为93.6%),除非你想用88个字节来编码63个字节。

我写了一篇文章 ,描述了一个完全处理你的问题的Python解决方案。 我没有使用Python的非常特殊的function来获得一个可以很容易地用其他语言实现的解决方案。 您可以查看一下,看看它是否符合您的需求。

CodeReview上的一篇文章促使我创建了一个RadixEncoding类,它能够处理字节数组到/从base-N字符串的编码/解码。

这个类可以在这个Q&A线程中找到 ,以及处理BigInteger,endian-ness支持和类的整体性能时的一些边缘情况的文档(和解决方案)