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

double.Parse("3.5") 

收益率为35和

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

抛出一个FormatException。

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

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


当前回答

我通常使用多区域性函数来解析用户输入,主要是因为如果某人习惯使用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文化中,我也需要解析将以点作为小数分隔符的数字。

其他回答

这里有一个解决方案,可以处理包含逗号和句点的任何数字字符串。这种解决方案特别适用于金额,因此只需要十分位和百分之一位。任何超过的数都被视为整数。

首先删除不是数字、逗号、句号或负号的任何内容。

string stringAmount = Regex.Replace(originalString, @"[^0-9\.\-,]", "");

然后我们把这个数分成整数和十进制数。

string[] decimalParsed = Regex.Split(stringAmount, @"(?:\.|,)(?=\d{2}$)");

(这个Regex表达式选择一个逗号或句号,它距离字符串的末尾有两个数字。)

现在我们取整个数字,去掉逗号和句号。

string wholeAmount = decimalParsed[0].Replace(",", "").Replace(".", "");

if (wholeAmount.IsNullOrEmpty())
        wholeAmount = "0";

现在我们处理小数部分。

string decimalAmount = "00";

if (decimalParsed.Length == 2)
    {
        decimalAmount = decimalParsed[1];
    }

最后,我们可以把整数和小数放在一起并解析Double。

double amount = $"{wholeAmount}.{decimalAmount}".ToDouble();

这将处理200、00、1 000、00、1,000、1.000、33、2,000.000、78等。

        var doublePattern = @"(?<integer>[0-9]+)(?:\,|\.)(?<fraction>[0-9]+)";
        var sourceDoubleString = "03444,44426";
        var match = Regex.Match(sourceDoubleString, doublePattern);

        var doubleResult = match.Success ? double.Parse(match.Groups["integer"].Value) + (match.Groups["fraction"].Value == null ? 0 : double.Parse(match.Groups["fraction"].Value) / Math.Pow(10, match.Groups["fraction"].Value.Length)): 0;
        Console.WriteLine("Double of string '{0}' is {1}", sourceDoubleString, doubleResult);

如果数值来自用户输入,我认为100%正确的转换是不可能的。例如,如果值是123.456,它可以是一个分组,也可以是一个小数点。如果你真的需要100%,你必须描述你的格式,如果不正确就抛出异常。

但我改进了JanW的代码,所以我们提前了一点,达到100%。背后的思想是,如果最后一个分隔符是groupseparator,它将更像是一个整数类型,而不是double类型。

添加的代码在GetDouble的第一个if中。

void Main()
{
    List<string> inputs = new List<string>() {
        "1.234.567,89",
        "1 234 567,89",
        "1 234 567.89",
        "1,234,567.89",
        "1234567,89",
        "1234567.89",
        "123456789",
        "123.456.789",
        "123,456,789,"
    };

    foreach(string input in inputs) {
        Console.WriteLine(GetDouble(input,0d));
    }

}

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

    // Check if last seperator==groupSeperator
    string groupSep = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
    if (value.LastIndexOf(groupSep) + 4 == value.Count())
    {
        bool tryParse = double.TryParse(value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out result);
        result = tryParse ? result : defaultValue;
    }
    else
    {
        // Unify string (no spaces, only . )
        output = value.Trim().Replace(" ", string.Empty).Replace(",", ".");

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

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

            // Combine token parts with last part
            output = string.Format("{0}.{1}", output, split.Last());
        }
        // Parse double invariant
        result = double.Parse(output, System.Globalization.CultureInfo.InvariantCulture);
    }
    return result;
}
double.Parse("3.5", CultureInfo.InvariantCulture)

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

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