【问题标题】:Parse String to Decimal using any decimal seperator使用任何小数分隔符将字符串解析为十进制
【发布时间】:2014-12-12 12:13:16
【问题描述】:

我想将文本输入中的字符串解析为十进制。该值表示货币值。

目前我得到了这个解决方案:

private Decimal CastToDecimal(string value) 
{
    Decimal result;
    var valid = Decimal.TryParse(value, NumberStyles.Currency, null, out result);
    return valid ? result : -1;
}

到目前为止,这工作得很好,除了可能的文化差异。我是德国人,我希望大多数用户输入德国风格的波动。但也有可能有人使用“.”。而不是“,”,转换将失败。

"123,45€" => 123.45

“123.456,78€” => 123456.78

"123.45€" => 12345

有没有办法自动检测所使用的文化以获取十进制值?这样无论你使用德语还是英语标点,你仍然得到相同的结果?

更新:

感谢您的帮助,我创建了一种方法,可以满足我的需求(我认为)。

private static Decimal CastToDecimal(string value)
{
    Decimal resultDe;
    Decimal resultEn;
    var style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands;
    var cultureDe = CultureInfo.CreateSpecificCulture("de-DE");
    var cultureEn = CultureInfo.CreateSpecificCulture("en-GB");
    var deValid = Decimal.TryParse(value, style, cultureDe, out resultDe);
    var enValid = Decimal.TryParse(value, style, cultureEn, out resultEn);
    var minVal = Math.Min(resultDe, resultEn);
    var maxVal = Math.Max(resultDe, resultEn);
    if (!deValid)
        return resultEn;
    if (!enValid)
        return resultDe;
    return BitConverter.GetBytes(decimal.GetBits(minVal)[3])[2] > 2 ? maxVal : minVal;
}

这段代码...

    Console.WriteLine(CastToDecimal("123,45"));
    Console.WriteLine(CastToDecimal("123.45"));
    Console.WriteLine(CastToDecimal("123,450"));
    Console.WriteLine(CastToDecimal("123.450"));
    Console.WriteLine(CastToDecimal("123.123,45"));
    Console.WriteLine(CastToDecimal("123,123.45"));

返回这个:

123,45
123,45
123450
123450
123123,45
123123,45

【问题讨论】:

  • 没有。您需要知道使用哪种文化来格式化它。或者在任何地方使用InvariantCulture。这是my answer 非常相关的问题。
  • 123.456 表示英国标点符号的 123.456 和德语的 123456!所以没有明确的方法来消除歧义。
  • 由于该字段仅用于货币,因此 123.456 将是 123456,因为没有半美分。如果分隔符右侧的数字少于 3 位,则仅是分隔符
  • @AlexanderMills 这可能适用于您的钱包,但不适用于某些计算产品平均价格的应用程序。
  • 是的,但是 decimal.Parse 不理解它只看到一个字符串的上下文。您应该能够获取用户的文化,然后传入适当的CultureInfo,以便解析正常工作和失败。

标签: c#


【解决方案1】:

http://msdn.microsoft.com/en-us/library/3s27fasw%28v=vs.110%29.aspx 的解决方案 其中包括设置 NumberStyle 可能会有所帮助。

...
value = "1.345,978";
style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands;
culture = CultureInfo.CreateSpecificCulture("es-ES");
if (Double.TryParse(value, style, culture, out number))
   Console.WriteLine("Converted '{0}' to {1}.", value, number);
else
   Console.WriteLine("Unable to convert '{0}'.", value);
// Displays:  
//       Converted '1.345,978' to 1345.978. 

value = "1 345,978";
if (Double.TryParse(value, style, culture, out number))
   Console.WriteLine("Converted '{0}' to {1}.", value, number);
else
   Console.WriteLine("Unable to convert '{0}'.", value);
...

【讨论】:

    【解决方案2】:

    我前段时间遇到了同样的问题。我的解决方案是用 Java 编写自己的解析器。该算法首先清理字符串。简要说明如下:

    1. 从左到右扫描字符串
    2. 如果 char = '.'然后 dotFound=true ; lastSeparatorPosition = 索引;点++
    3. 如果 char = ',' 那么 commaFound=true ; lastSeparatorPosition = 索引;逗号++
    4. 如果点 == 0 && 逗号 == 0 那么它是一个整数 => 完成
    5. 如果点 > 0 && 逗号 > 0 则 lastSeparatorPosition 是小数点分隔符。从字符串中删除其他 => 完成
    6. /* 只有一种分隔符类型 */ if (dots + commas) > 1 then remove them // 因为必须是千位分隔符 => 完成
    7. /* 分隔符出现一次 */ 如果 numberOfDigits 右侧的分隔符 == 3 则您必须决定 :-) 整数或小数,小数为 3 位

    7 是唯一剩下的问题,就像已经说过的 chiastic-security 一样。在这里,您只能决定考虑概念环境。所有其他情况都是安全的。

    玩得开心

    【讨论】:

    • 这只有在保证有小数点分隔符的情况下才有效。
    • 我不确定这是否可行。可能是我错过了一些东西。有数字组分隔符、小数分隔符等。你将如何消除它们的歧义?
    • 第一个非数字可以是千位分隔符
    【解决方案3】:

    这是做不到的,因为有些字符串在两种不同的文化中是有意义的,但意味着不同的东西。例如:

    123.456
    
    123,456
    

    第一个在英国是 123 多一点,但在德国是 123456;第二个是英国的 123456,但法国的 123 多一点。

    【讨论】:

    • 如果 OP 可以假设小数点后只有两位,这不是真的。
    • @juharr 也许可以根据这个假设工作,但对我来说似乎非常危险。
    【解决方案4】:

    唯一的解决方案是在输入上添加验证,如果是网页,则顺便给用户和示例,然后根据用户的文化找到一种获取输入的方法。我建议您不要尝试做您正在尝试的事情,因为有些文化相互矛盾,例如在货币方面;

    美国/澳大利亚/许多其他国家使用以下格式

    45,999.95
    

    其中 , 是千位分隔符, .是小数分隔符

    而在一些欧洲国家

    45.999,95  
    

    表示与上面相同,但千位分隔符是 .和 , 用作小数分隔符。

    现在的问题是不能保证用户同时使用分隔符,您的系统可能会将千位分隔符假定为十进制等等。

    如果您真的不想打扰用户,请为主要和次要货币分别设置输入字段。

    所以最好不要去那里。我相信这可能会有所帮助。快乐编码:)

    更新

    同样的情况是日期格式,例如在美国格式中,月份先到后天,而在澳大利亚,先到天再到现在02/01/2015 输入将意味着不同的系统无法说出用户的意图。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-02
      • 2015-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-18
      相关资源
      最近更新 更多