BigInteger到hex/十进制/八进制/二进制字符串?

在Java中,我能做到

BigInteger b = new BigInteger(500); 

然后按我的喜好格式化它

 b.toString(2); //binary b.toString(8); //octal b.toString(10); //decimal b.toString(16); //hexadecimal 

在C#中,我可以做到

 int num = int.Parse(b.ToString()); Convert.ToString(num,2) //binary Convert.ToString(num,8) //octal 

但我只能用long值和较小的值来做。 是否有一些方法来打印具有指定基数的BigInteger? 我发布了这个, BigInteger Parse Octal String? ,昨天收到了如何将基本上所有字符串转换为BigInteger值的解决方案,但还没有成功输出。

BigInteger转换为十进制,hex,二进制,八进制字符串:

让我们从BigInteger值开始:

 BigInteger bigint = BigInteger.Parse("123456789012345678901234567890"); 

基地10和基地16

内置Base 10(十进制)和base 16(hex)覆盖很容易:

 // Convert to base 10 (decimal): string base10 = bigint.ToString(); // Convert to base 16 (hexadecimal): string base16 = bigint.ToString("X"); 

前导零(正与负BigInteger值)

请注意, ToString("X")确保当BigInteger值为正时,hex字符串具有前导零。 这与ToString("X")的常规行为不同,当从其他值类型转换时,前导零被抑制。

例:

 var positiveBigInt = new BigInteger(128); var negativeBigInt = new BigInteger(-128); Console.WriteLine(positiveBigInt.ToString("X")); Console.WriteLine(negativeBigInt.ToString("X")); 

结果:

 080 80 

这种行为有一个目的,因为前导零表示BigInteger是一个正值 – 基本上,前导零提供了符号。 这是必要的(与其他值类型转换相反),因为BigInteger没有固定大小; 因此,没有指定的符号位。 前导零表示正值,而不是负值。 这允许通过ToString() BigInteger值“往返”并通过Parse() 。 在MSDN上的BigInteger Structure页面上讨论了此行为。

扩展方法:BigInteger到二进制,hex和八进制

这是一个包含将BigInteger实例转换为二进制,hex和八进制字符串的扩展方法的类:

 using System; using System.Numerics; using System.Text; ///  /// Extension methods to convert  /// instances to hexadecimal, octal, and binary strings. ///  public static class BigIntegerExtensions { ///  /// Converts a  to a binary string. ///  /// A . ///  /// A  containing a binary /// representation of the supplied . ///  public static string ToBinaryString(this BigInteger bigint) { var bytes = bigint.ToByteArray(); var idx = bytes.Length - 1; // Create a StringBuilder having appropriate capacity. var base2 = new StringBuilder(bytes.Length * 8); // Convert first byte to binary. var binary = Convert.ToString(bytes[idx], 2); // Ensure leading zero exists if value is positive. if (binary[0] != '0' && bigint.Sign == 1) { base2.Append('0'); } // Append binary string to StringBuilder. base2.Append(binary); // Convert remaining bytes adding leading zeros. for (idx--; idx >= 0; idx--) { base2.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0')); } return base2.ToString(); } ///  /// Converts a  to a hexadecimal string. ///  /// A . ///  /// A  containing a hexadecimal /// representation of the supplied . ///  public static string ToHexadecimalString(this BigInteger bigint) { return bigint.ToString("X"); } ///  /// Converts a  to a octal string. ///  /// A . ///  /// A  containing an octal /// representation of the supplied . ///  public static string ToOctalString(this BigInteger bigint) { var bytes = bigint.ToByteArray(); var idx = bytes.Length - 1; // Create a StringBuilder having appropriate capacity. var base8 = new StringBuilder(((bytes.Length / 3) + 1) * 8); // Calculate how many bytes are extra when byte array is split // into three-byte (24-bit) chunks. var extra = bytes.Length % 3; // If no bytes are extra, use three bytes for first chunk. if (extra == 0) { extra = 3; } // Convert first chunk (24-bits) to integer value. int int24 = 0; for (; extra != 0; extra--) { int24 <<= 8; int24 += bytes[idx--]; } // Convert 24-bit integer to octal without adding leading zeros. var octal = Convert.ToString(int24, 8); // Ensure leading zero exists if value is positive. if (octal[0] != '0' && bigint.Sign == 1) { base8.Append('0'); } // Append first converted chunk to StringBuilder. base8.Append(octal); // Convert remaining 24-bit chunks, adding leading zeros. for (; idx >= 0; idx -= 3) { int24 = (bytes[idx] << 16) + (bytes[idx - 1] << 8) + bytes[idx - 2]; base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0')); } return base8.ToString(); } } 

乍一看,这些方法似乎比必要的更复杂。 实际上,添加了一些额外的复杂性以确保转换后的字符串中存在正确的前导零。

让我们检查每个扩展方法,看看它们是如何工作的:

BigInteger.ToBinaryString()

以下是如何使用此扩展方法将BigInteger转换为二进制字符串:

 // Convert BigInteger to binary string. bigint.ToBinaryString(); 

每个扩展方法的基本核心是BigInteger.ToByteArray()方法。 此方法将BigInteger转换为字节数组,这是我们如何获取BigInteger值的二进制表示forms:

 var bytes = bigint.ToByteArray(); 

但要注意,返回的字节数组是小端序,因此第一个数组元素是BigInteger最低有效字节(LSB)。 由于StringBuilder用于构建输出字符串 - 从最高有效位(MSB)开始 - 必须反向迭代字节数组,以便首先转换最高有效字节。

因此,索引指针设置为字节数组中最高有效位(最后一个元素):

 var idx = bytes.Length - 1; 

要捕获转换后的字节,需要创建一个StringBuilder

 var base2 = new StringBuilder(bytes.Length * 8); 

StringBuilder构造函数获取StringBuilder的容量。 StringBuilder所需的容量是通过将要转换的字节数乘以8来计算的(每个转换的字节产生八个二进制数字)。

然后将第一个字节转换为二进制字符串:

 var binary = Convert.ToString(bytes[idx], 2); 

此时,如果BigInteger为正值,则必须确保存在前导零(参见上面的讨论)。 如果第一个转换的数字不是零,而bigint是正数,则将一个'0'附加到StringBuilder

 // Ensure leading zero exists if value is positive. if (binary[0] != '0' && bigint.Sign == 1) { base2.Append('0'); } 

接下来,转换后的字节将附加到StringBuilder

 base2.Append(binary); 

要转换剩余的字节,循环以相反的顺序迭代字节数组的其余部分:

 for (idx--; idx >= 0; idx--) { base16.Append(Convert.ToString(bytes[idx], 2).PadLeft(8, '0')); } 

请注意,根据需要,每个转换后的字节在左侧用零填充('0'),以便转换后的字符串为八个二进制字符。 这非常重要。 如果没有这个填充,hex值'101'将被转换为二进制值'11'。 前导零确保转换为“100000001”。

转换所有字节后, StringBuilder包含完整的二进制字符串,该字符串由扩展方法返回:

 return base2.ToString(); 

BigInteger.ToOctalString

BigInteger转换为八进制(基数为8)字符串更复杂。 问题是八进制数字代表三位,它不是由BigInteger.ToByteArray()创建的字节数组的每个元素中保存的八位的偶数倍。 为了解决这个问题,来自arrays的三个字节被组合成24位的块。 每个24位块均匀转换为八个八进制字符。

第一个24位块需要一些模数学:

 var extra = bytes.Length % 3; 

当整个字节数组被分成三字节(24位)块时,此计算确定多少字节为“额外”。 第一次转换为八进制(最高有效数字)获得“额外”字节,以便所有剩余的转换将分别获得三个字节。

如果没有“额外”字节,则第一个块获得完整的三个字节:

 if (extra == 0) { extra = 3; } 

第一个块被加载到一个名为int24的整数变量中,该变量int24可容纳24位。 块的每个字节都被加载。 当加载额外的字节时, int24中的先前位被左移8位以腾出空间:

 int int24 = 0; for (; extra != 0; extra--) { int24 <<= 8; int24 += bytes[idx--]; } 

通过以下方式将24位块转换为八进制:

 var octal = Convert.ToString(int24, 8); 

同样,如果BigInteger是正值,则第一个数字必须是前导零:

 // Ensure leading zero exists if value is positive. if (octal[0] != '0' && bigint.Sign == 1) { base8.Append('0'); } 

第一个转换的块附加到StringBuilder

 base8.Append(octal); 

其余的24位块在循环中转换:

 for (; idx >= 0; idx -= 3) { int24 = (bytes[idx] << 16) + (bytes[idx -1] << 8) + bytes[idx - 2]; base8.Append(Convert.ToString(int24, 8).PadLeft(8, '0')); } 

与二进制转换一样,每个转换的八进制字符串都用零填充,以便'7'变为'00000007'。 这样可以确保不会从转换后的字符串中间删除零(即“17”而不是“100000007”)。

转换为Base x?

BigInteger转换为其他数字基数可能要复杂得多。 只要数字基数是2的幂(即BigInteger.ToByteArray()BigInteger.ToByteArray()创建的字节数组就可以适当地分成比特块并进行转换。

但是,如果数字基数不是2的幂,则问题变得复杂得多,并且需要大量的循环和除法。 由于这种数字基数转换很少,我只在这里介绍了流行的计算数字基数。

经过一整天的BigInteger工作,我得到了更好的方法来输出二进制字符串,试试这个! (适用于负数)

 // Important note: when parsing hexadecimal string, make sure to prefix // with 0 if the number is positive. Ex: 0F instead of F, and 01A3 instead of 1A3. // If the number is negative, then the first bit should be set to 1. var x = BigInteger.Parse("0F", NumberStyles.HexNumber); // Or: BigInteger.Parse("15") var biBytes = x.ToByteArray(); var bits = new bool [8 * biBytes.Length]; new BitArray(x.ToByteArray()).CopyTo(bits, 0); bits = bits.Reverse().ToArray(); // BigInteger uses little endian when extracting bytes (thus bits), so we inverse them. var builder = new StringBuilder(); foreach(var bit in bits) { builder.Append(bit ? '1' : '0'); } string final = Regex.Replace(builder.ToString(), @"^0+", ""); // Because bytes consume full 8 bits, we might occasionally get leading zeros. Console.WriteLine(final); 

输出:1111

这是将BigInteger转换为任何基数的简单方法:

 public static string ToNBase(BigInteger a, int n) { StringBuilder sb = new StringBuilder(); while (a > 0) { sb.Insert(0,a % n); a /= n; } return sb.ToString(); } 

它适用于2-10基地。 如果您希望它生成hex或其他更高的基本字符串,您必须在插入之前根据基础修改a % b的表单。