универсальный разбор строк в float - PullRequest
5 голосов
/ 08 июля 2010

В среде, в которой будет запускаться моя программа, люди используют ',' и '.' как десятичные разделители случайным образом на ПК с ',' и '.' сепараторы.

Как бы вы реализовали такую ​​функцию с плавающей точкой (string)?

Я попробовал это:

    try
    {
        d = float.Parse(s);
    }
    catch
    {
        try
        {
            d = float.Parse(s.Replace(".", ","));
        }
        catch
        {
            d = float.Parse(s.Replace(",", "."));
        }
    }

Это не работает. И когда я отлаживаю, оказывается, что он разбирает его неправильно в первый раз, думая, что "." это разделитель для тысяч (например, 100.000.000,0).

Я новичок в C #, так что, надеюсь, есть менее сложное решение, чем: -)

NB: Люди собираются использовать оба '.' и ',' на ПК с различными настройками разделителя.

Ответы [ 3 ]

10 голосов
/ 08 июля 2010

Если вы уверены, что никто не использует разделители тысяч, вы можете сначала заменить:

string s = "1,23";  // or s = "1.23";

s = s.Replace(',', '.');
double d = double.Parse(s, CultureInfo.InvariantCulture);

Общая схема будет такой:

  • сначала попытайтесь санировать и нормализовать.Подобно Replace (otherChar, myChar).
  • попытайтесь определить формат, возможно, используя RegEx, и используйте целевое преобразование.Нравится считать.и, чтобы угадать, используются ли тысячи разделителей.
  • Попробуйте несколько форматов по порядку, с TryParse. не используйте исключения для этого.
2 голосов
/ 08 июля 2010

Синтаксический анализ использует настройки CultureInfo.CurrentCulture , которые отражают язык и формат числа, выбранные пользователем с помощью региональных настроек.Если ваши пользователи вводят десятичные дроби, соответствующие языку, который они выбрали для своих компьютеров, у вас не должно возникнуть проблем с использованием простого старого double.Parse ().Если пользователь устанавливает греческий язык и вводит «8,5», double.Parse («8,5») вернет 8.5.Если он введет «8,5», синтаксический анализ вернет 85.

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

Другой, несколько более строгий вариант - запретить группировочный символ для ввода номера в вашем приложении, отменив его в событии KeyDown ваших текстовых полей.Вы можете получить числовые и группирующие символы из свойства CultureInfo.CurrentCulture.NumberFormat .

Попытка замены десятичных символов и символов группировки обречена на неудачу, поскольку это зависит от знания во время компиляции, какого родаразделитель, который пользователь собирается использовать.Если бы вы знали это, вы могли бы просто проанализировать номер, используя CultureInfo в уме пользователя.К сожалению, User.Brain.CultureInfo еще не является частью .NET Framework: P

1 голос
/ 08 июля 2010

Я бы сделал что-то вроде этого

float ConvertToFloat(string value)
{
    float result;

    var converted  = float.TryParse(value, out result);

    if (converted) return result;

    converted = float.TryParse(value.Replace(".", ",")), 
                               out result);

    if (converted) return result;

    return float.NaN;
}

В этом случае верные данные вернутся

        Console.WriteLine(ConvertToFloat("10.10").ToString());
        Console.WriteLine(ConvertToFloat("11,0").ToString());
        Console.WriteLine(ConvertToFloat("12").ToString());
        Console.WriteLine(ConvertToFloat("1 . 10").ToString());

Returns

10,1
11
12
NaN

В этом случае, если его невозможно преобразовать, вы по крайней мере узнаете, что это не число. Это безопасный способ конвертации.

Вы также можете использовать следующую перегрузку

float.TryParse(value,
            NumberStyles.Currency,
            CultureInfo.CurrentCulture,
            out result)

по этому тест-коду:

Console.WriteLine(ConvertToFloat("10,10").ToString());
Console.WriteLine(ConvertToFloat("11,0").ToString());
Console.WriteLine(ConvertToFloat("12").ToString());
Console.WriteLine(ConvertToFloat("1 . 10").ToString());
Console.WriteLine(ConvertToFloat("100.000,1").ToString());

Возвращает следующее

10,1
11
12
110
100000,1

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

Это будет выглядеть так

float ConvertToFloat(string value)
{
    float result;

    var converted = float.TryParse(value,
                                    out result);


    if (converted) return result;

    converted = float.TryParse(value.Replace(".", ","),
                                out result);

    if (converted) return result;

    converted = float.TryParse(value,
                                    NumberStyles.Currency,
                                    CultureInfo.CurrentCulture,
                                    out result);

    return converted ? result : float.NaN;
}

Где следующее

Console.WriteLine(ConvertToFloat("10,10").ToString());
Console.WriteLine(ConvertToFloat("11,0").ToString());
Console.WriteLine(ConvertToFloat("12").ToString());
Console.WriteLine(ConvertToFloat("1 . 10").ToString());
Console.WriteLine(ConvertToFloat("100.000,1").ToString());
Console.WriteLine(ConvertToFloat("asdf").ToString());

Возвращает

10,1
11
12
110
100000,1
NaN
...