Как разобрать строку с десятичной точкой в ​​double?

Я хочу проанализировать строку типа "3.5" в double. Однако

double.Parse("3.5") 

дает 35 и

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

выбрасывает a FormatException.

Теперь мой языковой стандарт компьютера установлен на немецкий, где запятая используется как десятичный разделитель. Возможно, что-то нужно сделать с этим, а double.Parse() ожидает "3,5" в качестве ввода, но я не уверен.

Как я могу проанализировать строку, содержащую десятичное число, которое может быть отформатировано или не отформатировано в моей текущей локали?

Ответ 1

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

Ответ 2

Обычно я использую функцию мультикультуры для синтаксического анализа пользовательского ввода, главным образом потому, что, если кто-то используется для 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 мне нужно проанализировать число, которое будет иметь точку в качестве разделителя десятичных чисел.

Ответ 3

Трюк заключается в использовании инвариантной культуры, чтобы разобрать точку во всех культурах.

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

Ответ 4

Я не мог написать комментарий, поэтому пишу здесь:

double.Parse( "3.5", CultureInfo.InvariantCulture) не очень хорошая идея, потому что в Канаде мы пишем 3,5 вместо 3.5, и эта функция дает нам 35.

Я тестировал как на своем компьютере:

double.Parse("3.5", CultureInfo.InvariantCulture) --> 3.5 OK
double.Parse("3,5", CultureInfo.InvariantCulture) --> 35 not OK

Это правильный способ сказать Pierre-Alain Vigeant

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

Ответ 5

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

Заменить запятую точкой до разбора. Полезно в странах с запятой в качестве десятичного разделителя. Подумайте об ограничении ввода пользователем (при необходимости) в одну запятую или точку.

Ответ 6

Посмотрите, каждый ответ выше, который предлагает записать замену строки постоянной строкой, может быть ошибочным. Зачем? Потому что вы не уважаете региональные настройки Windows! Windows гарантирует, что пользователь имеет право устанавливать любой разделитель. S/He может открыть панель управления, перейти в область области, нажать на продвинутый и изменить символ в любое время. Даже во время вашей программы. Подумайте об этом. Хорошее решение должно быть в курсе этого.

Итак, сначала вам придется спросить себя, откуда этот номер, что вы хотите разобрать. Если он поступает из ввода в .NET Framework, это не проблема, потому что он будет в том же формате. Но, возможно, это происходило извне, возможно, с внешнего сервера, возможно, из старой БД, которая поддерживает только свойства строки. Там администратор db должен был дать правило, в котором формат должен быть сохранен. Если вы знаете, например, что это будет американская БД с американским форматом, вы можете использовать этот фрагмент кода:

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

Это будет прекрасно работать в любой точке мира. И, пожалуйста, не используйте "Convert.ToXxxx". Класс "Конвертировать" считается только базой для конверсий в любом направлении. Кроме того: вы также можете использовать аналогичный механизм для DateTimes.

Ответ 7

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

Ответ 8

Следующий код выполняет задание в любом сценарии. Это немного разбор.

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

Ответ 9

Я думаю, что 100% правильное преобразование невозможно, если значение исходит от пользовательского ввода. например если значение равно 123.456, оно может быть группировкой или может быть десятичной точкой. Если вам действительно нужно 100%, вам нужно описать свой формат и выбросить исключение, если оно неверно.

Но я улучшил код JanW, поэтому мы получаем немного больше вперед на 100%. Идея заключается в том, что если последний разделитель является groupSeperator, это будет более целочисленным типом, чем двойной.

Добавленный код находится в первом, если GetDouble.

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

Ответ 10

Я также улучшил код @JanW...

Мне нужно отформатировать результаты из медицинских инструментов, а также отправить " > 1000", "23.3e02", "350E-02" и "НЕГАТИВНО".

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

Ответ 11

Вместо того, чтобы указывать локаль во всех синтаксических разборах, я предпочитаю устанавливать локальную область приложения, хотя, если строковые форматы не являются согласованными в приложении, это может не сработать.

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

Определяя это в начале вашего приложения, все двойные парсы ожидают запятую как десятичный разделитель. Вы можете установить соответствующий языковой стандарт, чтобы разделитель десятичных чисел и тысяч соответствовал строкам, которые вы обрабатываете.

Ответ 12

Это сложно, не указывая, какой десятичный разделитель искать, но если вы это сделаете, это то, что я использую:

    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", ',');
    }

Это должно работать с любой культурой. Он правильно не разбирает строки, содержащие более одного разделителя с разделителями, в отличие от реализаций, которые заменяют вместо swap.

Ответ 13

Умножьте число, а затем разделите его на то, на что вы его умножали раньше.

Например,

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

Ответ 14

System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.CurrentCulture;

string _pos = dblstr.Replace(".",
    ci.NumberFormat.NumberDecimalSeparator).Replace(",",
        ci.NumberFormat.NumberDecimalSeparator);

double _dbl = double.Parse(_pos);

Ответ 15

Ниже ниже эффективность, но я использую эту логику. Это справедливо, только если у вас есть две цифры после десятичной точки.

double val;

if (temp.Text.Split('.').Length > 1)
{
    val = double.Parse(temp.Text.Split('.')[0]);

    if (temp.Text.Split('.')[1].Length == 1)
        val += (0.1 * double.Parse(temp.Text.Split('.')[1]));
    else
        val += (0.01 * double.Parse(temp.Text.Split('.')[1]));
}
else
    val = double.Parse(RR(temp.Text));

Ответ 16

Я думаю, что это лучший ответ:

public static double StringToDouble(string toDouble)
{
    toDouble = toDouble.Replace(",", "."); //Replace every comma with dot

    //Count dots in toDouble, and if there is more than one dot, throw an exception.
    //Value such as "123.123.123" can't be converted to double
    int dotCount = 0;
    foreach (char c in toDouble) if (c == '.') dotCount++; //Increments dotCount for each dot in toDouble
    if (dotCount > 1) throw new Exception(); //If in toDouble is more than one dot, it means that toCount is not a double

    string left = toDouble.Split('.')[0]; //Everything before the dot
    string right = toDouble.Split('.')[1]; //Everything after the dot

    int iLeft = int.Parse(left); //Convert strings to ints
    int iRight = int.Parse(right);

    //We must use Math.Pow() instead of ^
    double d = iLeft + (iRight * Math.Pow(10, -(right.Length)));
    return d;
}