C# 如何将带小数点的字符串解析为双精度值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1354924/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 15:32:33  来源:igfitidea点击:

How do I parse a string with a decimal point to a double?

c#stringparsingdouble

提问by

I want to parse a string like "3.5"to a double. However,

我想将字符串解析"3.5"为双精度。然而,

double.Parse("3.5") 

yields 35 and

产生 35 和

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

throws a FormatException.

抛出一个FormatException.

Now my computer's locale is set to German, wherein a comma is used as decimal separator. It might have to do something with that and double.Parse()expecting "3,5"as input, but I'm not sure.

现在我的计算机的语言环境设置为德语,其中逗号用作小数点分隔符。它可能必须对此做些什么并double.Parse()期望"3,5"作为输入,但我不确定。

How can I parse a string containing a decimal number that may or may not be formatted as specified in my current locale?

如何解析包含十进制数字的字符串,该数字可能会或可能不会按照我当前语言环境中的指定进行格式化?

回答by Mehrdad Afshari

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

回答by Yakeen

The trick is to use invariant culture, to parse dot in all cultures.

诀窍是使用不变文化,在所有文化中解析点。

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

回答by Pierre-Alain Vigeant

I usualy use a multi-culture function to parse user input, mostly because if someone is used to the numpad and is using a culture that use a comma as the decimal separator, that person will use the point of the numpad instead of a comma.

我通常使用多文化函数来解析用户输入,主要是因为如果有人习惯了小键盘并且使用使用逗号作为小数点分隔符的文化,那么该人将使用小键盘的点而不是逗号。

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

Beware though, @nikie comments are true. To my defense, I use this function in a controlled environment where I know that the culture can either be en-US, en-CA or fr-CA. I use this function because in French, we use the comma as a decimal separator, but anybody who ever worked in finance will always use the decimal separator on the numpad, but this is a point, not a comma. So even in the fr-CA culture, I need to parse number that will have a point as the decimal separator.

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

回答by Baluda

Double.Parse("3,5".Replace(',', '.'), CultureInfo.InvariantCulture)

Replace the comma with a point before parsing. Useful in countries with a comma as decimal separator. Think about limiting user input (if necessary) to one comma or point.

在解析之前用点替换逗号。在使用逗号作为小数点分隔符的国家/地区很有用。考虑将用户输入(如有必要)限制为一个逗号或一个点。

回答by JanW

The following code does the job in any scenario. It's a little bit parsing.

以下代码在任何情况下都可以完成这项工作。它有点解析。

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

回答by Schorsch

I think 100% correct conversion isn't possible, if the value comes from a user input. e.g. if the value is 123.456, it can be a grouping or it can be a decimal point. If you really need 100% you have to describe your format and throw an exception if it is not correct.

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

But I improved the code of JanW, so we get a little bit more ahead to the 100%. The idea behind is, that if the last separator is a groupSeperator, this would be more an integer type, than a double.

但是我改进了 JanW 的代码,所以我们在 100% 上更领先一点。背后的想法是,如果最后一个分隔符是 groupSeperator,这将更像是一个整数类型,而不是一个双精度型。

The added code is in the first ifof GetDouble.

添加的代码在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;
}

回答by JacekK

I improved the code of @JanW as well...

我也改进了@JanW 的代码......

I need it to format results from medical instruments, and they also send ">1000", "23.3e02", "350E-02", and "NEGATIVE".

我需要它来格式化医疗仪器的结果,他们还发送“>1000”、“23.3e02”、“350E-02”和“NEGATIVE”。

private string FormatResult(string vResult)
{
  string output;
  string input = vResult;

  // 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());
  }
  string sfirst = output.Substring(0, 1);

  try
  {
    if (sfirst == "<" || sfirst == ">")
    {
      output = output.Replace(sfirst, "");
      double res = Double.Parse(output);
      return String.Format("{1}{0:0.####}", res, sfirst);
    }
    else
    {
      double res = Double.Parse(output);
      return String.Format("{0:0.####}", res);
    }
  }
  catch
  {
    return output;
  }
}

回答by Martin

string testString1 = "2,457";
string testString2 = "2.457";    
double testNum = 0.5;
char decimalSepparator;
decimalSepparator = testNum.ToString()[1];

Console.WriteLine(double.Parse(testString1.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));
Console.WriteLine(double.Parse(testString2.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));

回答by Miguel Mesquita Alfaiate

Instead of having to specify a locale in all parses, I prefer to set an application wide locale, although if string formats are not consistent across the app, this might not work.

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

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

Defining this at the begining of your application will make all double parses expect a comma as the decimal delimiter. You can set an appropriate locale so that the decimal and thousands separator fits the strings you are parsing.

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

回答by AndresRohrAtlasInformatik

Look, every answer above that proposes writing a string replacement by a constant string can only be wrong. Why? Because you don't respect the region settings of Windows! Windows assures the user to have the freedom to set whatever separator character s/he wants. S/He can open up the control panel, go into the region panel, click on advanced and change the character at any time. Even during your program run. Think of this. A good solution must be aware of this.

看,上面建议用常量字符串编写字符串替换的每个答案都只能是错误的。为什么?因为你不尊重Windows的区域设置!Windows 确保用户可以自由设置他/她想要的任何分隔符。他/她可以打开控制面板,进入区域面板,点击高级,随时更换角色。即使在您的程序运行期间。想想这个。一个好的解决方案必须意识到这一点。

So, first you will have to ask yourself, where this number is coming from, that you want to parse. If it's coming from input in the .NET Framework no problem, because it will be in the same format. But maybe it was coming from outside, maybe from a external server, maybe from an old DB that only supports string properties. There, the db admin should have given a rule in which format the numbers are to be stored. If you know for example that it will be an US DB with US format you can use this piece of code:

所以,首先你必须问自己,这个数字来自哪里,你想解析。如果它来自 .NET Framework 中的输入,没问题,因为它将采用相同的格式。但也许它来自外部,也许来自外部服务器,也许来自仅支持字符串属性的旧数据库。在那里,数据库管理员应该给出一个规则,以何种格式存储数字。例如,如果您知道它将是美国格式的 US DB,则可以使用以下代码:

CultureInfo usCulture = new CultureInfo("en-US");
NumberFormatInfo dbNumberFormat = usCulture.NumberFormat;
decimal number = decimal.Parse(db.numberString, dbNumberFormat);

This will work fine anywhere on the world. And please don't use 'Convert.ToXxxx'. The 'Convert' class is thought only as a base for conversions in any direction. Besides: You may use the similar mechanism for DateTimes too.

这在世界任何地方都可以正常工作。并且请不要使用“Convert.ToXxxx”。'Convert' 类仅被视为任何方向转换的基础。此外:您也可以对 DateTimes 使用类似的机制。