我想把“3.5”这样的字符串解析为double。然而,

double.Parse("3.5") 

收益率为35和

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint) 

抛出一个FormatException。

现在我的计算机的区域设置为德语,其中逗号用作小数分隔符。它可能需要做一些事情,double.Parse()期望“3,5”作为输入,但我不确定。

如何解析包含十进制数字的字符串,该十进制数字可能是或可能不是我当前地区指定的格式?


当前回答

下面的代码在任何场景下都可以完成这项工作。这是一点解析。

List<string> inputs = new List<string>()
{
    "1.234.567,89",
    "1 234 567,89",
    "1 234 567.89",
    "1,234,567.89",
    "123456789",
    "1234567,89",
    "1234567.89",
};
string output;

foreach (string input in inputs)
{
    // Unify string (no spaces, only .)
    output = input.Trim().Replace(" ", "").Replace(",", ".");

    // Split it on points
    string[] split = output.Split('.');

    if (split.Count() > 1)
    {
        // Take all parts except last
        output = string.Join("", split.Take(split.Count()-1).ToArray());

        // Combine token parts with last part
        output = string.Format("{0}.{1}", output, split.Last());
    }

    // Parse double invariant
    double d = double.Parse(output, CultureInfo.InvariantCulture);
    Console.WriteLine(d);
}

其他回答

我通常使用多区域性函数来解析用户输入,主要是因为如果某人习惯使用numpad,并且正在使用使用逗号作为小数分隔符的区域性,那么此人将使用numpad的点而不是逗号。

public static double GetDouble(string value, double defaultValue)
{
    double result;

    //Try parsing in the current culture
    if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
        //Then try in US english
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
        //Then in neutral language
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
    {
        result = defaultValue;
    }

    return result;
}

不过要注意,@nikie的评论是真的。我要为自己辩护的是,我在受控环境中使用这个函数,我知道文化可以是en-US、en-CA或fr-CA。我用这个函数是因为在法语中,我们用逗号作为小数分隔符,但任何在金融领域工作过的人都会在numpad上使用小数分隔符,但这是一个点,而不是一个逗号。因此,即使在fr-CA文化中,我也需要解析将以点作为小数分隔符的数字。

如果没有指定要查找的小数分隔符,这是很困难的,但如果你这样做了,这就是我使用的:

    public static double Parse(string str, char decimalSep)
    {
        string s = GetInvariantParseString(str, decimalSep);
        return double.Parse(s, System.Globalization.CultureInfo.InvariantCulture);
    }

    public static bool TryParse(string str, char decimalSep, out double result)
    {
        // NumberStyles.Float | NumberStyles.AllowThousands got from Reflector
        return double.TryParse(GetInvariantParseString(str, decimalSep), NumberStyles.Float | NumberStyles.AllowThousands, System.Globalization.CultureInfo.InvariantCulture, out result);
    }

    private static string GetInvariantParseString(string str, char decimalSep)
    {
        str = str.Replace(" ", "");

        if (decimalSep != '.')
            str = SwapChar(str, decimalSep, '.');

        return str;
    }
    public static string SwapChar(string value, char from, char to)
    {
        if (value == null)
            throw new ArgumentNullException("value");

        StringBuilder builder = new StringBuilder();

        foreach (var item in value)
        {
            char c = item;
            if (c == from)
                c = to;
            else if (c == to)
                c = from;

            builder.Append(c);
        }
        return builder.ToString();
    }

    private static void ParseTestErr(string p, char p_2)
    {
        double res;
        bool b = TryParse(p, p_2, out res);
        if (b)
            throw new Exception();
    }

    private static void ParseTest(double p, string p_2, char p_3)
    {
        double d = Parse(p_2, p_3);
        if (d != p)
            throw new Exception();
    }

    static void Main(string[] args)
    {
        ParseTest(100100100.100, "100.100.100,100", ',');
        ParseTest(100100100.100, "100,100,100.100", '.');
        ParseTest(100100100100, "100.100.100.100", ',');
        ParseTest(100100100100, "100,100,100,100", '.');
        ParseTestErr("100,100,100,100", ',');
        ParseTestErr("100.100.100.100", '.');
        ParseTest(100100100100, "100 100 100 100.0", '.');
        ParseTest(100100100.100, "100 100 100.100", '.');
        ParseTest(100100100.100, "100 100 100,100", ',');
        ParseTest(100100100100, "100 100 100,100", '.');
        ParseTest(1234567.89, "1.234.567,89", ',');    
        ParseTest(1234567.89, "1 234 567,89", ',');    
        ParseTest(1234567.89, "1 234 567.89",     '.');
        ParseTest(1234567.89, "1,234,567.89",    '.');
        ParseTest(1234567.89, "1234567,89",     ',');
        ParseTest(1234567.89, "1234567.89",  '.');
        ParseTest(123456789, "123456789", '.');
        ParseTest(123456789, "123456789", ',');
        ParseTest(123456789, "123.456.789", ',');
        ParseTest(1234567890, "1.234.567.890", ',');
    }

这对任何文化都适用。它正确地无法解析具有多个小数分隔符的字符串,这与replace而不是swap的实现不同。

乘以这个数,然后除以之前乘以的数。

例如,

perc = double.Parse("3.555)*1000;
result = perc/1000

诀窍是使用不变区域性,来解析所有区域性中的dot。

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.NumberFormatInfo.InvariantInfo);

而不是必须在所有解析中指定一个语言环境,我更喜欢设置一个应用程序范围内的语言环境,尽管如果字符串格式在应用程序中不一致,这可能不起作用。

CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-PT");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("pt-PT");

在应用程序的开始定义它将使所有双解析都期望逗号作为小数分隔符。您可以设置适当的区域设置,以便十进制和千位分隔符适合您正在解析的字符串。