背景:我最近有幸编写了将字符串可靠地转换为国际双打的代码.此功能也必须分发.即字符串存储在数据库中,需要在运行在各种语言环境中的众多代理上转换为数字.出于限制原因,更改数据库模式是不可能的,我必须在遗留代码库中使用简单的升级路径并且不破坏现有功能.
我能够通过将存储的字符串规范化为不变格式并向编码添加标志来指示该值是否已规范化并且应该采用新路径还是非规范化(sp?)并采用传统路径来解决此问题.
我忘了提到原始值是由最终用户输入的,并且必须在可接受的格式范围内.意味着存储的值可能有也可能没有数字分组说明符.显然这很危险,它目前只适用于测试版,并且正确的国际化用户界面很快就会发布,以便正确发布.
这就是说我认为我的转换代码应该能够处理数字分组字符是合理的,即使最终的规范化形式不包括它们. 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));
}
测试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中的行为)
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,它将失败.
问:有人可以解释为什么会这样吗?
运行理论:我当前的两个想法是一个字符编码错误,其中包含数字分组字符或者引擎盖下的实际双重表示对于那些特定的文化是不同的(类似于双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);
我将结束这个问题,但如果你有任何问题,请随时回复任何问题.“