将带有逗号和小数点的字符串解析为双精度数字

103

我正在尝试编写一个函数,它可以将字符串数组转换为另一个字符串数组,其中所有数组中的double类型值都被四舍五入到指定小数位数。该数组中也可能会包含非double类型的字符串。

string[,] values = new string[1, 3];

values[0, 0] = "hello";
values[0, 1] = "0.123";
values[0, 2] = "0,123";

int decimalPlaces = 2;

double tmp;
string format = "F" + decimalPlaces.ToString();
IFormatProvider provider = CultureInfo.InvariantCulture;
for (int i = 0; i < values.GetLength(0); i++)
{
    for (int j = 0; j < values.GetLength(1); j++)
    {
        if (double.TryParse(values[i, j], out tmp))
        {
            values[i, j] = tmp.ToString(format, provider);
        }
    }
}

Console.ReadLine();

结果应该是:"hello", "0.12", "0.12",但实际上是"hello", "123.00", "0.12"。逗号被错误地处理了。有没有简单而高效的解决方案?


3
正如Hultqvist在评论中指出的那样,如果当前文化使用点"."作为小数点,那么目前被接受的答案会出错!因此,您是否介意将接受的答案更改为投票最多的答案? - Stefnotch
15个回答

200
为了将逗号和句点都视为小数点,您不仅需要用其中一个替换另一个,还需要确保所使用的解析文化将其解释为小数点。
text = text.Replace(',', '.');
return double.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out value);

2
这可能会导致千位分隔符出现问题。 - Retic
1
@Retic 的确,一个文化中的千位分隔符在另一个文化中可能是小数点分隔符。 - hultqvist
1
@hultqvist 我猜在拉丁西班牙文化中,“1,000”会被替换为“1”。而期望的是“1000”。 - Alan Mattano
5
@alan-mattano 这个解决方案是针对一个具体问题的。并没有适用于所有文化的单一解决方案,你必须先检测正在使用的文化,然后相应地选择算法。 - hultqvist

67

您不需要替换逗号和句点。

我遇到了同样的问题。原因很简单,转换文化在解释逗号或句点时起着重要作用。我使用德国文化,逗号区分小数,而在其他地方,句点则是这个作用。这里我提供了一个完整的示例,以明确差异。

string[] doubleStrings = {"hello", "0.123", "0,123"};
double localCultreResult;
foreach (var doubleString in doubleStrings)
{
    double.TryParse(doubleString, NumberStyles.Any, CultureInfo.CurrentCulture, out localCultreResult);
    Console.WriteLine(string.Format("Local culture results for the parsing of {0} is {1}", doubleString, localCultreResult));
}

double invariantCultureResult;
foreach (var doubleString in doubleStrings)
{
    double.TryParse(doubleString, NumberStyles.Any, CultureInfo.InvariantCulture, out invariantCultureResult);
    Console.WriteLine(string.Format("Invariant culture results for the parsing of {0} is {1}", doubleString, invariantCultureResult));
}

结果如下所示: 请输入图像描述

尝试调整文化,您将获得所需的结果。


我在几台不同文化设置的电脑上尝试了这个解决方案。它并没有像预期的那样工作。稍后我会发布另一个解决方案。 - Andrew_STOP_RU_WAR_IN_UA
我尝试了这个,但对我不起作用。请查看链接中的图片。CurrentCulture参数是德语。https://imgur.com/QvtPt7S - Emil
3
这应该是被接受的答案。如果需要更严格地限制,您也可以将NumberStyles.AllowDecimalPoint作为第二个参数使用。这对我很有效! - Matt
这将在法国电脑上将1.5转换为15。 - undefined

27

您想把点号(.)视为逗号(,)。所以,将

if (double.TryParse(values[i, j], out tmp))

使用

if (double.TryParse(values[i, j].Replace('.', ','), out tmp))

45
解析时需要指定文化背景,否则仅在当前文化使用“,”作为小数点的情况下才能正常工作。 - hultqvist
1
好的。谢谢你的警告。 - mmdemirbas
4
我知道这个讨论串早已不活跃,但我建议使用replace(char, char)代替Replace(string, string)以提高性能。 - unknown6656
4
替换是个坏主意,因为在具有不同文化设置的电脑上,应用程序会失败……我稍后会写出更好的修复方案。 - Andrew_STOP_RU_WAR_IN_UA

19
问题在于您(或系统)无法区分逗号或者点作为千位分隔符还是小数点。例如:
在我的文化中,“1.123”是一个大于1000的普通表示,而“1,123”则是接近1的数字。
使用不变文化默认使用点作为小数点分隔符。通常情况下,应确保所有数字都在所有系统上使用相同的不变文化(例如,不变文化)进行编写。
如果您确定您的数字除了逗号或点作为小数点分隔符外,不包含其他任何内容(即没有千位分隔符),我建议使用`String.Replace()`将逗号替换为点,并且按照原来的方式继续处理。
否则,您将很难编写程序来区分`1.123`和`1,123`,除非您知道这个数字的文化背景。

同意,对我来说这是最合乎逻辑的解决方案。你也可以检查字符串是否包含“,”或“.”,但在我看来,这两种解决方案都应该可以。 - eandersson
1
“...在所有系统上…” 超出了我的控制范围。 - R. Schreurs

7

创建两个静态文化,一个用于逗号,一个用于小数点。

    var commaCulture = new CultureInfo("en")
    {
        NumberFormat =
        {
            NumberDecimalSeparator = ","
        }
    };

    var pointCulture = new CultureInfo("en")
    {
        NumberFormat =
        {
            NumberDecimalSeparator = "."
        }
    };

然后根据输入(使用函数)分别使用它们:

    public double ConvertToDouble(string input)
    {
        input = input.Trim();

        if (input == "0") {
            return 0;
        }

        if (input.Contains(",") && input.Split(',').Length == 2)
        {
            return Convert.ToDouble(input, commaCulture);
        }

        if (input.Contains(".") && input.Split('.').Length == 2)
        {
            return Convert.ToDouble(input, pointCulture);
        }

        throw new Exception("Invalid input!");
    }

然后遍历你的数组。
    var strings = new List<string> {"0,12", "0.122", "1,23", "00,0", "0.00", "12.5000", "0.002", "0,001"};
    var doubles = new List<double>();

    foreach (var value in strings) {
        doubles.Add(ConvertToDouble(value));
    }

这应该能够正常工作,即使主机环境和文化发生变化。

1
不是这样的,如果你有1,000,000.20,它会失败。 - IggyBar
或者“1000”失败 - Sahin
谢谢!这个对我有用。但是当字符串不包含“.”或“,”,而只有数字时会发生异常。小修复可以解决问题。 - Mikhail Kh

7

简单用法:

double.Parse("3.5", CultureInfo.InvariantCulture)

1
虽然这个代码可以运行,但是解释为什么很重要。请阅读此S.O.帖子以了解InvariantCulture是什么:https://dev59.com/cmkw5IYBdhLWcg3wn7-O - sɐunıɔןɐqɐp

2

1

从字符串中解析十进制数的扩展。

  • No matter number will be on the beginning, in the end, or in the middle of a string.
  • No matter if there will be only number or lot of "garbage" letters.
  • No matter what is delimiter configured in the cultural settings on the PC: it will parse dot and comma both correctly.
  • Ability to set decimal symbol manually.

    public static class StringExtension
    {
        public static double DoubleParseAdvanced(this string strToParse, char decimalSymbol = ',')
        {
            string tmp = Regex.Match(strToParse, @"([-]?[0-9]+)([\s])?([0-9]+)?[." + decimalSymbol + "]?([0-9 ]+)?([0-9]+)?").Value;
    
            if (tmp.Length > 0 && strToParse.Contains(tmp))
            {
                var currDecSeparator = System.Windows.Forms.Application.CurrentCulture.NumberFormat.NumberDecimalSeparator;
    
                tmp = tmp.Replace(".", currDecSeparator).Replace(decimalSymbol.ToString(), currDecSeparator);
    
                return double.Parse(tmp);
            }
    
            return 0;
        }
    }
    
如何使用:

"It's 4.45 O'clock now".DoubleParseAdvanced(); // will return 4.45
"It's 4,45 O'clock now".DoubleParseAdvanced(); // will return 4.45
"It's 4:45 O'clock now".DoubleParseAdvanced(':'); // will return 4.45

2
4:45 应该等于 4.75 :P - Logman
对于非WinForms应用程序,请使用: var currDecSeparator = Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator; - Per G
1
当你使用'.'作为decimalSymbol参数时,这将无效。如果你的currentCulture.NumberDecimalSeperator是',', Assert.AreEqual(4.45d, DoubleParseAdvanced("4.45", '.')); 将返回445。 - SHEePYTaGGeRNeP
是的,你说得对。需要更新代码。也许稍后会做。 - Andrew_STOP_RU_WAR_IN_UA
你在两种文化中都测试过这个“。”和“,”吗? - Alan Mattano

0

对于我来说,使用CultureInfo不是一个选择,因为应用程序在不同文化的系统上运行,但要解析的输入字符串的文化是未知的。

因此,我参考了通过规范化字符串表示然后使用CultureInfo.InvariantCulture进行转换:

    private static double NormalizeAndParse(string strDouble)
    {
        string strDoubleNormalized;

        if (strDouble.Contains(","))
        {
            var strReplaced = strDouble.Replace(",", ".");
            var decimalSeparatorPos = strReplaced.LastIndexOf('.');
            var strInteger = strReplaced.Substring(0, decimalSeparatorPos);
            var strFractional = strReplaced.Substring(decimalSeparatorPos);

            strInteger = strInteger.Replace(".", string.Empty);
            strDoubleNormalized = strInteger + strFractional;
        }
        else
        {
            strDoubleNormalized = strDouble;
        }

        return Double.Parse(strDoubleNormalized, NumberStyles.Any, CultureInfo.InvariantCulture);
    }

0

如果我想解析一个带有小数点分隔符但没有其他内容的数字,我会使用以下代码片段:

public bool TryParseDecimal(string value, out decimal result) {
    const string your_separator = ",";

    var numberFormat = new NumberFormatInfo {
            NumberDecimalSeparator = your_separator
    };

    return decimal.TryParse(value, NumberStyles.AllowDecimalPoint, numberFormat, out result);
}

我认为使用文化或字符串操作并不能表达将具有非 '.' 小数点的数字转换的意图。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接