c# - .NET Double.TryParse(num, format, cultureinfo, out) 错误

标签 c# .net vb.net internationalization double

背景:我最近有幸编写了需要在国际范围内可靠地将字符串转换为 double 的代码。此功能也必须分发。即字符串存储在数据库中,需要在跨不同区域设置的众多代理上转换为数字。出于限制原因,更改数据库模式是不可能的,我必须在遗留代码库中使用简单的升级路径并且不破坏现有功能来完成这项工作。

我能够通过将存储的字符串规范化为不变格式并在编码中添加一个标志来指示该值是否已规范化以及应该采用新路径还是非规范化(sp?)并采用旧路径来解决这个问题路径。

我忘了提到原始值是由最终用户输入的,并且必须在可接受的格式范围内。这意味着存储的值可能有也可能没有数字分组说明符。显然这是危险的,它目前仅适用于测试版,并且预计很快就会对 UI 进行适当的国际化以进行适当的发布。

也就是说,我认为我的转换代码应该能够处理数字分组字符是合理的,即使最终的规范化形式不包括它们。带有适当文化格式的 Double.TryParse() 和 Double.ToString() 应该可以毫无问题地处理这个问题,并且可以出于其他原因重用转换代码(是的遗留代码!)。

.NET 错误 所以我认为围绕国际化字符串编写一些单元测试是个好主意,以双重转换代码。

我写了两个主要测试(一种伪代码)。

测试 1:

Double testValue = 15000.05
foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures) 
{
    string testString = testValue.ToString(ci);
    Assert.AreEqual(testValue, Convert(testString, ci));
}

测试 2:

foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures) 
{
    string testString = testValue.ToString("N2", ci);
    Assert.AreEqual(testValue, Convert(testString, ci));
}

相关转换代码(几乎一行一行):

If Not Double.TryParse(numIn, Globalization.NumberStyles.Any, cultureInfo, numOut) Then Return False

为了测试,收集所有文化代码的确切方法可能不同,Convert 的方法签名不同,周围的代码和断言略有不同。 相关部分是 .ToString(ci) 和 .ToString("N2", ci)。 对于 en-US,这些版本将分别生成“15000.05”和“15,000.05”。 此外,此代码在 .NET 版本 2.0 - 4.5.2 下运行,我们在各种相关版本下运行测试。它在所有方面的行为都相同(*可能需要仔细检查,但这绝对是 .NET 4.5.2 中的行为)

测试 1 通过!

测试 2 在这 5 个文化代码上失败:

  • 公关人员
  • prs-AF
  • zm
  • tzm-拉丁语
  • tzm-Latn-DZ

我们目前正在忽略这些不受支持的故障,并跟踪是否出现我们关心的任何新故障。

诊断 在四处挖掘和试验一些之后,我们将问题追溯到数字分组说明符。即千位分隔符。 将 Double.TryParse() 更改为

numOut = Double.Parse(numIn, ci)

有效。所以这个问题特别与 Double.TryParse() 有关,并且可能与 NumberStyle.Any 说明符有关。 Or'ing 它与十六进制说明符也不起作用。

因此我们在 .NET 中遇到了这样一种情况,您可以使用特定的 IFormatProvider 将 double 型转换为字符串,然后尝试使用相同的 IFormatProvider 将其转换回 double 型,但会失败。

问题:谁能解释为什么会这样?

运行理论:我目前的两个想法是数字分组字符的字符编码错误,或者那些特定文化的实际 double 表示是不同的(类似于 double x = 0.3 是在 .NET 中真的是 0.299...)。

免责声明:我在 VB.NET 和 C# 之间切换,所以请原谅任何语法混淆。另外,我知道该测试没有正确说明“奇数”数字分组,例如在印地语中 1,015,000 写成 10,15,000。

最佳答案

@tarekgh 在 GitHub issue 上发布了一个答案.以下是他写的:

“这里的问题是失败的文化,因为你有以下几点:

小数点分隔符是“,” 组分隔符是“.” 货币小数点分隔符是“.” 货币组分隔符为“,” 请注意,小数分隔符与货币组分隔符相同。组分隔符与货币小数点分隔符相同。

现在当您用这种文化格式化数字时,您将得到字符串“15.000,05”。当您尝试解析它时,您传递的是 NumberStyles.Any,这意味着该字符串可以是货币数字,也可以是十进制数字。这会在尝试解析字符“.”时混淆解析器。因为它可以被视为货币小数点分隔符,也可以被视为组分隔符。解析器决定将其视为货币小数点分隔符。然后解析器将继续直到命中“,”并再次将其视为货币组分隔符。因为组分隔符不能出现在小数分隔符之后,解析器将无法解析字符串并从 TryParse 返回 false(或从 Parse 抛出异常)。

解决此问题的方法是从传递的 NumberStyles 中删除货币解析。即

        Double.TryParse(numString, NumberStyles.Any & (~NumberStyles.AllowCurrencySymbol), ci, out numParsed);

我将关闭这个问题,但如果您有任何问题,请随时回复。”

关于c# - .NET Double.TryParse(num, format, cultureinfo, out) 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44559594/

相关文章:

VB.NET - 实现 IDisposable 时是否应该添加 Finalize 方法?

vb.net - 在 VB.NET 中声明一个字节数组

vb.net - 使用 Reflection.PropertyInfo

c# - 使用WFA接口(interface)插入数据时出现sqlException

c# - LINQ:添加 RowNumber 列

c# - 如何处理 EF 4.3.1 设置修改了 Rowversion 行

c# - Moq 到 FakeItEasy 的迁移 - 如何迁移Verify()?

c# - EF、抽象和通用扩展编译器在这里做什么?

c# - 如何使用单键查找优化更新 C# 字典?

c# - 您必须添加对程序集 mscorlib 的引用,version=4.0.0