【问题标题】:Number parsing weirdness数字解析怪异
【发布时间】:2010-01-19 14:47:42
【问题描述】:

这行代码:

Console.WriteLine(Convert.ToInt32(“23,23”) + 1);

抛出异常。 这行代码:

Console.WriteLine(Convert.ToDouble(“23,23”) + 1);

打印 2324。

有人知道为什么会这样吗?我认为第二次转换不会带来任何好处。

【问题讨论】:

  • 这只是表明如果你想避免意外,你应该使用带有 NumberStyles 和 IFormatProvider 的重载。

标签: c#


【解决方案1】:

来自the MSDN documentation of System.Double.Parse

s 参数可以包含 [...] 形式的字符串:

[ws][sign][integral-digits[,]]integral-digits[.[fractional-digits]][e[sign]exponential-digits][ws]

在这里,逗号 (,) 代表“[a] 文化特定的千位分隔符”。

总结一下:如果您当前文化的千位分隔符出现在字符串中的任何位置,它会被Double.Parse 忽略(由Convert.ToDouble 在内部调用)。


另一方面,Int32.Parse(string) 不允许字符串中有千位分隔符:

[ws][sign]digits[ws]

这就是您的第一个示例引发异常的原因。正如其他答案所解释的那样,您可以使用允许您指定 NumberStyles 的重载来更改 Double.ParseInt32.Parse 的此行为。

【讨论】:

  • 但是为什么 Integer.Parse 会有所不同呢?千位分隔符在这里不是同样有意义吗?
  • @Konrad:我更新了我的答案。 Int32.Parse 只允许[ws][sign]digits[ws]。关于这背后的动机:不知道......
  • 我猜这只是实现上的差异(这背后可能有也可能没有一些基本原理)。 MSDN 文档说Int32.Parse 只接受[ws][sign]digits[ws],其中ws 是空格。默认情况下它不接受千位运算符...
  • "另一方面,Int32.Parse 不允许千位分隔符":int.Parse 方法确实 支持千位分隔符,它只是默认禁用。看我的回答。
【解决方案2】:

第一个失败是因为int.Parse 默认不允许千位分隔符。您可以使用NumberStyles 更改它:

int d = int.Parse("11,23",
                  NumberStyles.AllowThousands,
                  CultureInfo.InvariantCulture);

现在它就像双重版本一样,确实默认支持千位分隔符。它们可能会成功,因为“,”在被解析器视为千位分隔符时会被完全忽略 - 即使逗号有时作为千位分隔符没有意义。

令人惊讶的是,即使这样也有效:

double d = double.Parse("1,,1,2,3", CultureInfo.InvariantCulture);

在上面,d 设置为值 1123.0。

【讨论】:

  • 由于在提供的示例中, 后面只有两位数字,所以它不是更有可能是小数分隔符吗?
  • @Jørn:解释为逗号分隔符还是小数分隔符由 CultureInfo 确定。我可以从 quillbreaker 的第二个示例的输出中看到,逗号是他的千位分隔符,否则输出将是“24,23”。
【解决方案3】:
Console.WriteLine(Convert.ToDouble(“23,23”) + 1);

在这种情况下,逗号被解释为您的本地化的组分隔符,并被忽略。见http://msdn.microsoft.com/en-us/library/fd84bdyt.aspx

Console.WriteLine(Convert.ToInt32(“23,23”) + 1);

在这种情况下,您使用的是Int32.Parse,默认情况下不支持组分隔符。

这背后的原因是整数转换器默认不支持本地化,因为本地化会增加额外的开销,并且没有理由为不需要交互的解析器添加它任何符号。但是,您可以通过一些额外的参数强制解析器支持本地化:

int.Parse("11,23", NumberStyles.AllowThousands, CultureInfo.InvariantCulture);

浮点/双精度转换,另一方面,支持小数分隔符。在某些文化中,这是",",在其他文化中,它可以是" ""."。由于该功能无论如何都必须支持本地化,因此默认情况下仅支持某些本地化功能是没有意义的。否则,该实现会使那些期望由于小数分隔符支持本地化的人感到困惑,它也会支持其他本地化方面。

【讨论】:

  • int.Parse does 支持千位分隔符 - 请参阅我的答案。
  • +1 用于解释推理!您可能想写“...default 整数转换器...”,因为Int32.Parse(string, NumberStyles) 允许您添加本地化支持。
  • +1 用于推理,但话又说回来,我想知道为什么双重 Parse(默认)允许文化解析的开销?
  • @Keugyeol 还有什么其他选择?小数按定义需要有分隔符来区分整数部分和小数部分。
  • @David 好吧,是的,双重的默认解析器甚至会检查诸如“NaN”之类的东西,但是检查千位分隔符可能是一个额外的负担(无论它有多大) .所以这让我想知道,如果是整数解析器,那么性能优于一致性的决定背后的原因是什么。
【解决方案4】:

在转换为双精度时忽略逗号。如果您希望逗号生成错误,则可以使用 Double.Parse(string, System.Globalization.NumberStyles) 方法。

【讨论】:

  • 如果逗号(数字组分隔符)放在小数点之后,它会给出格式异常,所以我猜它并没有真正被忽略,而是以某种方式考虑。
【解决方案5】:

因为双精度假设逗号是千位分隔符,并忽略它。 Int32 Convert 不这样做。

【讨论】:

    【解决方案6】:
    ?double.Parse("23,23", System.Globalization.CultureInfo.InstalledUICulture);
    23.23
    
    ?double.Parse("23,23", new System.Globalization.CultureInfo("en-US"));
    2323.0
    ?double.Parse("23,23", new System.Globalization.CultureInfo("fr-FR"));
    23.23
    
    ?double.Parse("23,23", System.Globalization.CultureInfo.InvariantCulture);
    2323.0
    

    Convert.ToDouble 也一样:

    ?Convert.ToDouble("23,23", System.Globalization.CultureInfo.InvariantCulture);
    2323.0
    

    【讨论】:

      猜你喜欢
      • 2015-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-26
      • 1970-01-01
      相关资源
      最近更新 更多