BigInteger.Parse() 大量读取时遇到问题

BigInteger.Parse() trouble reading in large numbers

目前我正在尝试完成这个挑战 (http://cryptopals.com/sets/1/challenges/1),但我在用 C# 完成任务时遇到了一些问题。我似乎无法将数字解析为一个大整数。

因此代码如下所示:

        string output = "";
        BigInteger hexValue = BigInteger.Parse("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6");

        output = Convert.ToBase64String(hexValue.ToByteArray());
        Console.WriteLine(hexValue);
        Console.WriteLine(output);
        Console.ReadKey();
        return "";

目前我遇到的问题是当我运行程序失败并出现错误

System.FormatException: 'The value could not be parsed.' and I am not entirely sure why.

那么,将字符串中的大整数转换为 BigInt 的合适方法是什么?

问题是输入的不是十进制而是十六进制,所以需要额外传递一个参数进行解析:

BigInteger number = BigInteger.Parse(
            hexString,
            NumberStyles.AllowHexSpecifier);

使用NumberStyles.HexNumber:

BigInteger.Parse("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6", 
                 NumberStyles.HexNumber,
                 CultureInfo.InvariantCulture);

如果您的数字应该始终为正数,请在您的字符串中添加一个前导零。

初始问题

BigInteger.Parse 方法期望值是十进制的,而不是十六进制的。您可以 "fix" 通过传入 NumberStyles.HexNumber.

使用BigInteger的更大问题

如果您只是想将一串十六进制数字转换为字节,我会完全避免使用 BigInteger。一方面,例如,如果原始字节数组以零开头,您最终可能会遇到问题。零不会在生成的字节数组中。 (示例输入:“0001”——你想输出两个字节,但在说服它解析十六进制后你只会得到一个。)

即使您没有丢失任何信息,您从BigInteger.ToByteArray() 收到的byte[] 也可能不是您所期望的。例如,考虑这段代码,它只是将数据转换为 byte[] 并通过 BitConverter:

转换回十六进制
BigInteger bigInt = BigInteger.Parse("1234567890ABCDEF", NumberStyles.HexNumber);
byte[] bytes = bigInt.ToByteArray();
Console.WriteLine(BitConverter.ToString(bytes));

它的输出是 "EF-CD-AB-90-78-56-34-12" - 因为 BigInteger.ToByteArray returns little-endian 顺序中的数据:

The individual bytes in the array returned by this method appear in little-endian order. That is, the lower-order bytes of the value precede the higher-order bytes.

这不是你想要的 - 因为它意味着原始字符串的 last 部分是字节数组的 first 部分,等等

完全避免BigInteger

相反,将数据 直接 解析为字节数组,如 this question, or this one 或其他各种形式。我不会在这里重现代码,但它很简单,根据您是要创建简单的源代码还是要创建高效的程序,有不同的选项。

关于转化的一般建议

一般来说,避免数据的中间表示是个好主意,除非您绝对确信您不会在此过程中丢失信息 - 正如您在这里所做的那样。在将结果转换为 base64 之前将十六进制字符串转换为字节数组很好,因为这不是有损转换。

所以您的转化是:

  • String(十六进制)到 BigInteger:有损(在前导 0 很重要的情况下,就像在这种情况下一样)
  • BigIntegerbyte[]:无损
  • byte[]String (base64):无损

我推荐:

  • String(十六进制)到 byte[]:无损(假设您有偶数个字节要转换,这通常是一个合理的假设)
  • byte[]String (base64):无损