2009-08-30 1 views
172

Я хочу разобрать строку, как "3.5", на двойной. Тем не менее,Как разобрать строку с десятичной точкой в ​​double?

double.Parse("3.5") 

дает 35 и

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

бросает FormatException.

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

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

+0

Десятичная запятая, безусловно, влияет на выход. – ChrisF

+9

Не забывайте о методе double.TryParse(), если это подходит для вашей ситуации. –

ответ

334
double.Parse("3.5", CultureInfo.InvariantCulture) 
+0

Мне нравится использовать класс 'XmlConvert' ... у вас есть идеи, лучше ли это, хуже и/или отличаться от использования' CultureInfo.InvariantCulture'? – ChrisW

+1

Ну, 'XmlConvert' на самом деле не предназначен для синтаксического анализа одного двойного значения в коде. Я предпочитаю использовать 'double.Parse' или' Convert.ToDouble', которые делают мое намерение очевидным. –

+4

Это означает, что doulble.Parse использует культуру по умолчанию, которая не может содержать точку в виде десятичной точки? –

15

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

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

Не работает во всех сценариях. 1.234.567,89 – JanW

+0

@JanW, в котором это не работает? – BendEg

60

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

+13

Я не уверен, что это хорошая идея. Вы не можете достоверно разобрать двойной, если вы не знаете культуру: в Германии двойные значения могут содержать «.», Но они считаются тысячами-разделителями. Таким образом, в случае с Legate GetDouble («3.5») вернет 35 в немецком языке и 3.5 в среде en-us. – Niki

+0

Нет, Пьер Ален прав, поскольку это точно так же, как написано. Если ваша культура говорит, что «точка - тысяча» разделителей, то «3.5» рассматривается как «35», и это хорошо. Однако, если вы культивируете как никакие правила для «точки», тогда символ анализируется как десятичная точка, и он также работает. Я бы не стал пытаться использовать enUS-культуру, но это личный выбор. – xryl669

+0

Если вы используете numpad в культуре с запятой, точечная клавиша будет установлена ​​запятая. – CrazyBaran

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

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

+1

Гораздо более правильный ответ, чем тот, который имеет +133 голосов ... Он позволяет жить в обеих системах с помощью «,» или «.». десятичный разделитель ... – Badiboy

+0

@ Бадибой вы можете объяснить, что не так с этим ответом? Как я понимаю, InvariantCulture всегда имеет. как десятичный разделитель. Поэтому он должен работать для обеих систем. – AlexP11223

+0

@ Alex11223 Вы правы. Вот почему я сказал, что этот ответ лучше, чем более популярный. PS: Дружелюбный говорящий этот код также потерпит неудачу, если у вас есть «,» как LIST SEPARATOR (т. Е. 1,234,560.01), но я не знаю, как это решить вообще. :) – Badiboy

1

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

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

1.234.567.890 вернется 1234567.890 –

+0

Я не пробовал, но если вы выполните приложение в разных SO SO культуры, этот код не будет делать трюк, я думаю:/ –

1

Я думаю, что 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

я улучшил код @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; 
    } 
} 
2
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))); 
+2

Это не сработает для стран, которые используют тысячу разделителей. – noggin182

0

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

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

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

6

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

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

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

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

+0

Согласен! Попытка вручную реализовать функции Культуры в конечном итоге приведет к тому, что вы не ожидали, и большой головной боли. Пусть .NET справится с этим должным образом. – Khalos

+1

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

0

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

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.

-2

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

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

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

Например,

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

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

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

Re: * «... потому что в Канаде мы пишем 3,5 вместо 3,5» * Вы уверены в этом? Согласно * [Десятичная метка] (https://en.wikipedia.org/wiki/Decimal_mark#Countries_using_Arabic_numerals_with_decimal_point): «Страны, в которых точка». »Используется в качестве десятичной метки, включает ... Канада (при использовании английского)» * , Разве это не больше об использовании французской версии Windows? –

+0

Baybe по причине французская версия. В montreal пишем 3,5 не 3,5 –

+0

Итак, согласно вашей логике, всегда только 1 из них возвращает true? – batmaci

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

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

double _dbl = double.Parse(_pos); 
-2

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

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

Объяснение кода и предоставление более подробной информации было бы полезно. –

+0

Что здесь объяснить? Все в комментариях – Endorphinex