Как мне разобрать строку с десятичной точкой в ​​двойном? - PullRequest
201 голосов
/ 31 августа 2009

Я хочу разобрать строку типа "3.5" в двойную. Тем не менее,

double.Parse("3.5") 

дает 35 и

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

бросает FormatException.

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

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

Ответы [ 18 ]

378 голосов
/ 31 августа 2009
double.Parse("3.5", CultureInfo.InvariantCulture)
67 голосов
/ 31 августа 2009

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

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

20 голосов
/ 18 марта 2016

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

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

Это правильный способ, которым Пьер-Ален Вижан упомянул

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;
}
15 голосов
/ 31 августа 2009

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

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.NumberFormatInfo.InvariantInfo);
14 голосов
/ 15 октября 2009
Double.Parse("3,5".Replace(',', '.'), CultureInfo.InvariantCulture)

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

9 голосов
/ 04 июля 2015

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

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

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

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

3 голосов
/ 15 октября 2012
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)));
1 голос
/ 21 августа 2010

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

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);
}
1 голос
/ 08 февраля 2012

Я думаю, что 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;
}
0 голосов
/ 25 декабря 2018

Мои два цента на эту тему, пытаясь предоставить общий метод двойного преобразования:

private static double ParseDouble(object value)
{
    double result;

    string doubleAsString = value.ToString();
    IEnumerable<char> doubleAsCharList = doubleAsString.ToList();

    if (doubleAsCharList.Where(ch => ch == '.' || ch == ',').Count() <= 1)
    {
        double.TryParse(doubleAsString.Replace(',', '.'),
            System.Globalization.NumberStyles.Any,
            CultureInfo.InvariantCulture,
            out result);
    }
    else
    {
        if (doubleAsCharList.Where(ch => ch == '.').Count() <= 1
            && doubleAsCharList.Where(ch => ch == ',').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(",", string.Empty),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else if (doubleAsCharList.Where(ch => ch == ',').Count() <= 1
            && doubleAsCharList.Where(ch => ch == '.').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(".", string.Empty).Replace(',', '.'),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else
        {
            throw new ParsingException($"Error parsing {doubleAsString} as double, try removing thousand separators (if any)");
        }
    }

    return result;
}

Работает, как и ожидалось, с:

  • 1,1
  • 1,1
  • 1000000000
  • 1.000.000.000
  • 1,000,000,000.99
  • 1.000.000.000,99
  • 5,000,111.3
  • 5.000.111,3
  • 0.99,000,111,88
  • 0,99.000.111.88

Преобразование по умолчанию не реализовано, поэтому он не сможет выполнить анализ 1.3,14, 1,3.14 или подобных случаев.

...