【问题标题】:Cross-regional decimal/double parsing跨区域十进制/双精度解析
【发布时间】:2013-09-06 21:31:11
【问题描述】:

事实上,我有多个可以生成数字数据的系统,它们以文本文件的形式存储在某个 Web 服务器上。有些系统使用小数点作为分数分隔符,有些系统使用小数点逗号作为相同。

应用程序(胖客户端,.net 2.0)也可以在任何一种系统上运行。

所以在一些磕磕绊绊之后我这样做了:(http://pastebin.com/vhLXABDD)

    public static bool HasDecimalComma;
    public static bool HasDecimalPeriod;

    public static double GetNumber(string NumberString)
    {
        if (!HasDecimalComma && !HasDecimalPeriod)
        {
            string s = string.Format("{0:0.0}", 123.123);
            if (s.Contains('.'))
            {
                HasDecimalPeriod = true;
            }
            else if (s.Contains(','))
            {
                HasDecimalComma = true;
            }
            else
            {
                throw new SystemException(string.Format("strange number format '{0}'", s));
            }
        }
        if (HasDecimalComma)
        {
            return double.Parse(NumberString.Replace('.', ','));
        }
        if (HasDecimalPeriod)
        {
            return double.Parse(NumberString.Replace(',', '.'));
        }
        throw new ArgumentException(string.Format("can't parse '{0}'", NumberString));
    }

您能提出更好、更优雅的方法吗?

编辑:

我很抱歉之前没有提到它,因为你的答案倾向于那个方向 - 我不能用数字存储生成文化,我只能尝试“检测”它。

【问题讨论】:

  • 你能用数字字符串存储生成的文化吗?这将减轻对这种字符串弯曲的任何需求。
  • 你大致知道生成的数字是多少吗?例如,归结为一组选项?我不确定您的系统将如何处理解析“1,000.12”(实际上我知道,它会中断)。如果您的可能格式数量有限,那么您可以尝试每种格式,前提是它们没有歧义的可能性。
  • 我很幸运,所以永远不会出现千位分隔符。
  • 所以你可以保证你的号码是一串数字,可能有一个前导减号和一个“.”,一个“,”或者在字符串的某个地方都没有?
  • 总是,总是,总是使用不变的文化来序列化浮点数。持久化日期应始终采用已知形式。如果不是,你就失败了。 msdn.microsoft.com/en-us/library/…

标签: c# parsing regional-settings


【解决方案1】:

试试这个:

    static double GetDouble(string s)
    {
        double d;

        var formatinfo = new NumberFormatInfo();

        formatinfo.NumberDecimalSeparator = ".";

        if (double.TryParse(s, NumberStyles.Float, formatinfo, out d))
        {
            return d;
        }

        formatinfo.NumberDecimalSeparator = ",";

        if (double.TryParse(s, NumberStyles.Float, formatinfo, out d))
        {
            return d;
        }

        throw new SystemException(string.Format("strange number format '{0}'", s));
    }

【讨论】:

  • 这在.被用作千位分隔符的情况下可能不起作用。
  • 显然不是问题,正如 cmets 中已澄清的那样(问题),所以这看起来不错。
【解决方案2】:

如果您无法更改调用站点,并且您保证在小数分隔符旁边不会出现其他类型的分隔符,那么您可以或多或少地使用您的方法。我建议

  1. М将 HasDecimalCommaHasDecimalPeriod 移动到方法的主体 - 在这种情况下,绝对不需要全局状态。
  2. 使用TryParse 而不是Parse,因为预计这些数字可能存在错误。
  3. 明确指定InvariantCulture 区域性(它有一个小数点)。
  4. 允许既没有逗号也没有句点的数字,因为“3”毕竟是一个浮点数。

所以大致如下:

///comment the method assumptions here
///otherwise the method might seem wrong
public static double GetNumber(string numberString)
{
   bool hasDecimalComma = numberString.Contains(',');
   if (hasDecimalComma)
     numberString = numberString.Replace(',', '.')
   double result;
   bool success = double.TryParse(numberString, 
                      NumberStyles.Float, 
                      CultureInfo.InvariantCulture,
                      out result);
   if (success)
     return result;
   else 
     throw new ArgumentException(
                           string.Format("can't parse '{0}'", numberString));
}

(旧答案,原则上很好,实践中不可能)

我建议将生成的区域性存储在字符串中,然后使用它来调用方法,按照这些思路(使用double.TryParse):

public static double GetNumber(string numberString, CultureInfo culture)
{
   double result;
   bool success = double.TryParse(numberString, 
                          NumberStyles.Float | NumberStyles.AllowThousands, 
                          culture,
                          out result);
   if (success)
      return result;
   else 
      throw new ArgumentException(
                               string.Format("can't parse '{0}'", numberString));
}

【讨论】:

  • 据此更改了答案。
【解决方案3】:

只需使用当前的文化和正确的数字格式标志。当然,您必须针对可能存储在数据库中的所有文化对其进行测试。更好:将数字转换为 CultureInfo.Invariant 文化,然后再将它们存储到数据库中。或者:存储数字时也存储文化标识符。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            const string toTest = "1.006,30";

            float number;
            if (float.TryParse(toTest, NumberStyles.AllowDecimalPoint | NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out number))
                Console.WriteLine("Success: {0}", number);
            else
                Console.WriteLine("Failure: strange number format");

            Console.WriteLine("Press any key to finish");
            Console.ReadKey();
        }
    }
}

【讨论】:

    【解决方案4】:

    除非您的某些客户使用波斯语(fafa-IR 标签)作为他们的文化,否则我认为您很适合:

    // apparently, Persians use '/' as the decimal separator
    var slash = CultureInfo
        .GetCultures(CultureTypes.AllCultures)
        .Where(c => 
            c.NumberFormat.NumberDecimalSeparator != "," && 
            c.NumberFormat.NumberDecimalSeparator != ".")
        .ToList();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-11
      • 1970-01-01
      • 2014-02-07
      • 2017-02-27
      相关资源
      最近更新 更多