【问题标题】:BigInteger.Parse() trouble reading in large numbersBigInteger.Parse() 大量读取问题
【发布时间】:2018-09-18 01:41:13
【问题描述】:

目前我正在尝试完成这个挑战 (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: '无法解析该值。' 我不完全确定原因。

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

【问题讨论】:

  • @EhsanSajjad:它是一个整数,但它是十六进制而不是十进制。

标签: c# parsing biginteger


【解决方案1】:

最初的问题

BigInteger.Parse 方法要求该值为十进制,而不是十六进制。您可以通过传入 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以小端顺序返回数据:

此方法返回的数组中的各个字节以 little-endian 顺序出现。也就是说,值的低位字节在高位字节之前。

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

完全避免BigInteger

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

关于转化的一般建议

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

所以你的转化是:

  • String(十六进制)到 BigInteger:有损(在前导 0 很重要的情况下,因为它们在这种情况下)
  • BigIntegerbyte[]:没有损耗
  • byte[]String (base64):没有损耗

我推荐:

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

【讨论】:

  • 我非常困惑:BigInteger 的十六进制字符串不会有损。 BigInteger 也不是字节数组。符号不会丢失,而是编码在顶部字节中(这就是为什么有时,对于正值,必须返回额外的顶部 0 字节,或者对于负值,必须返回额外的 0xFF 字节)。而-0x800000000x80000000不同,转换为BigInteger时。
  • @RudyVelthuis:是的,它有损的。 “00000001”被解析为与“01”相同的值,当您尝试将十六进制字符串解析为二进制数据时,这是一个问题。第一个值代表 4 个字节,第二个值代表 1 个字节。它们都解析为相同的BigInteger,因此信息已丢失。你说得对,ToByteArray 不会丢失信息 - 我会为此进行编辑。
  • @RudyVelthuis 如果十六进制字符串表示数字,则不会有损,但在这种情况下,十六进制字符串表示字节数组(因为目标是将其转换为 base64 字符串)。当然,在这种情况下使用大整数根本没有意义,但这就是这个答案所说的。
  • 我看到在他链接的挑战中,他必须转换为Base64。那么他的做法确实是错误的。
【解决方案2】:

使用NumberStyles.HexNumber:

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

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

【讨论】:

  • 尽管如此,这仍然可能会丢失信息,因为 OP 从根本上尝试解析为字节数组,而不是整数 - 他们只是选择了 via整数,这是一个糟糕的选择 IMO。例如,您的代码“0001”最终会产生一个字节。
【解决方案3】:

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

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

【讨论】:

  • @CodesInChaos:尽管有名字,但似乎是这样。 (以我在回答中提到的问题为模。)事实上,文档指出“使用此样式解析的字符串不能以“0x”或“&h”为前缀。所以它的名字真的非常糟糕,因为它不允许使用十六进制说明符。
猜你喜欢
  • 2012-10-22
  • 2017-11-30
  • 1970-01-01
  • 1970-01-01
  • 2020-04-03
  • 2022-11-25
  • 2023-03-17
  • 1970-01-01
  • 2023-01-18
相关资源
最近更新 更多