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

时间:2017-06-15 05:54:32

标签: c# .net vb.net internationalization double

背景:我最近有幸编写了将字符串可靠地转换为国际双打的代码。此功能也必须分发。即字符串存储在数据库中,需要在运行在各种语言环境中的众多代理上转换为数字。出于限制原因,改变数据库模式是不可能的,我不得不在遗留代码库中使用简单的升级路径并且不破坏现有功能。

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

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

那就是说我认为我的转换代码应该能够处理数字分组字符是合理的,即使最终的规范化形式不会包含它们。提供正确的文化格式的Double.TryParse()和Double.ToString()在处理此问题时应该没有问题,并且转换代码可能由于其他原因而重复使用(yay遗留代码!)。

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

我写了两个主要的测试(psuedo代码的种类)。

测试1:

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

的Test2:

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中的行为)

Test1通过!

Test2在这5个文化代码上失败了:

  • PRS
  • PRS-AF
  • TZM
  • TZM-LATN
  • TZM-LATN-DZ

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

诊断 在挖掘并尝试一些之后,我们将问题追溯到数字分组说明符。即千分之一的地方分隔符。 将Double.TryParse()更改为

numOut = Double.Parse(numIn, ci)

作品。所以问题特别是Double.TryParse(),可能与NumberStyle.Any说明符有关。或者使用十六进制说明符进行操作也不起作用。

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

问题:任何人都可以解释为什么会这样吗?

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

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

1 个答案:

答案 0 :(得分:1)

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

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

小数分隔符是"," 组分隔符是"。" 货币小数分隔符是"。" 货币组分隔符是"," 请注意,小数分隔符与货币组分隔符相同。组分隔符也与货币小数分隔符相同。

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

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

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

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