Cross-regional decimal/double parsing

…衆ロ難τιáo~ 提交于 2019-12-22 07:04:38

问题


As a matter of fact, I have multiple systems that can generate numeric data and they are stored on some web server in text files. Some of the systems use decimal point as fraction separator, some of the systems use decimal comma as same.

Applications (fat client, .net 2.0) can also be run on either kind of systems.

So after some stumbling I did this: ( 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));
    }

Would you suggest any better, more elegant way?

EDIT:

I am sorry for not mentioning it before, and since your answers lean in that direction - I can't store generating culture with the numbers, I can only try to 'detect' it.


回答1:


Try with this:

    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));
    }



回答2:


If you are unable to change the call site, and you guarantee that no other kind of separator will be present beside the decimal separator, then you can, more or less use your method. I would suggest

  1. Мoving the HasDecimalComma and HasDecimalPeriod to the body of the method - global state is absolutely not necessary in this case.
  2. Using TryParse instead of Parse, because the numbers are expected to be potentially faulty.
  3. Explitly specifying the InvariantCulture culture (it has a decimal period).
  4. Allowing for number that have neither comma nor period, since "3" is a floating point number after all.

So something along these lines:

///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));
}

(Old answer, good in principle, impossible in practice)

I would suggest storing the generating culture along the string, and then using it to call the method, along these lines (using 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:


Just use the current culture and correct number format flags. Of couse, you have to test it against all cultures that are potentially stored in your database. Better: convert numbers to CultureInfo.Invariant culture before even storing them into the database. Or: Store also the culture identifier when storing the number.

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:


Unless some of your clients is using Persian (fa, fa-IR tag) as their culture, I think you're good to go:

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


来源:https://stackoverflow.com/questions/18571579/cross-regional-decimal-double-parsing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!