Best way to convert string to decimal separator "." and "," insensitive way?
Solution 1
You have the following possibllities:
- You know the culture
- Use the current Culture setting, for which the computer is installed
- You let the user decide to set his culture -> user settings in your program
- You do not know the culture
- You must decide about it: you have to define and document your decision
- Guess: you try to parse, and try to parse, and try to ... until you get valid numbers
Solution 2
You can create a temporary CultureInfo
object to use when you parse.
// get a temporary culture (clone) to modify
var ci = CultureInfo.InvariantCulture.Clone() as CultureInfo;
ci.NumberFormat.NumberDecimalSeparator = ",";
decimal number = decimal.Parse("1,1", ci); // 1.1
Solution 3
I found another way to do it. It looks odd but it works fine for me.
So if you don't know culture of the target system and you don't know which value you will get like 12.33 or 12,33 you can do following
string amount = "12.33";
// or i.e. string amount = "12,33";
var c = System.Threading.Thread.CurrentThread.CurrentCulture;
var s = c.NumberFormat.CurrencyDecimalSeparator;
amount = amount.Replace(",", s);
amount = amount.Replace(".", s);
decimal transactionAmount = Convert.ToDecimal(amount);
Solution 4
You just need to have the correct culture set, when calling Parse
, like so:
string s = "11,20";
decimal c1 = decimal.Parse(s, new CultureInfo("fr-FR"));
decimal c2 = decimal.Parse(s, new CultureInfo("en-AU"));
Console.WriteLine(c1);
Console.WriteLine(c2);
Solution 5
Below is my implementation, any good idear?
/// <summary>
///
/// </summary>
public static class NumberExtensions
{
/// <summary>
/// Convert string value to decimal ignore the culture.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>Decimal value.</returns>
public static decimal ToDecimal ( this string value )
{
decimal number;
string tempValue = value;
var punctuation = value.Where ( x => char.IsPunctuation ( x ) ).Distinct ( );
int count = punctuation.Count ( );
NumberFormatInfo format = CultureInfo.InvariantCulture.NumberFormat;
switch ( count )
{
case 0:
break;
case 1:
tempValue = value.Replace ( ",", "." );
break;
case 2:
if ( punctuation.ElementAt ( 0 ) == '.' )
tempValue = value.SwapChar ( '.', ',' );
break;
default:
throw new InvalidCastException ( );
}
number = decimal.Parse ( tempValue, format );
return number;
}
/// <summary>
/// Swaps the char.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="from">From.</param>
/// <param name="to">To.</param>
/// <returns></returns>
public static string SwapChar ( this 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 ( );
}
}
[TestClass]
public class NumberTest
{
/// <summary>
///
/// </summary>
[TestMethod]
public void Convert_To_Decimal_Test ( )
{
string v1 = "123.4";
string v2 = "123,4";
string v3 = "1,234.5";
string v4 = "1.234,5";
string v5 = "123";
string v6 = "1,234,567.89";
string v7 = "1.234.567,89";
decimal a1 = v1.ToDecimal ( );
decimal a2 = v2.ToDecimal ( );
decimal a3 = v3.ToDecimal ( );
decimal a4 = v4.ToDecimal ( );
decimal a5 = v5.ToDecimal ( );
decimal a6 = v6.ToDecimal ( );
decimal a7 = v7.ToDecimal ( );
Assert.AreEqual ( ( decimal ) 123.4, a1 );
Assert.AreEqual ( ( decimal ) 123.4, a2 );
Assert.AreEqual ( ( decimal ) 1234.5, a3 );
Assert.AreEqual ( ( decimal ) 1234.5, a4 );
Assert.AreEqual ( ( decimal ) 123, a5 );
Assert.AreEqual ( ( decimal ) 1234567.89, a6 );
Assert.AreEqual ( ( decimal ) 1234567.89, a7 );
}
/// <summary>
///
/// </summary>
[TestMethod]
public void Swap_Char_Test ( )
{
string v6 = "1,234,567.89";
string v7 = "1.234.567,89";
string a1 = v6.SwapChar ( ',', '.' );
string a2 = v7.SwapChar ( ',', '.' );
Assert.AreEqual ( "1.234.567,89", a1 );
Assert.AreEqual ( "1,234,567.89", a2 );
}
}
Related videos on Youtube
Comments
-
Andrew Florko almost 2 years
Application deals with strings that represent decimals that come from different cultures. For example "1.1 and "1,1" is the same value.
I played with
Decimal.TryParse
flags combinations but couldn't achieve the result I want. "1,1" became "11" or "0" after all.Is it possible to convert such strings to decimal in one line of code without pre-replacing "," char to "." or playing with
NumberFormat.NumberDecimalSeparator
?How do you handle such situations?
Thank you in advance!
-
Kobi over 13 yearsHow can you be sure what
1,234
means? -
Andrew Florko over 13 yearsIt's the floating point value between 1 and 2 :) "1.234" is the same floating value
-
dlanod over 13 yearsSeconded what Kobi posted first up. 1,234 is one and two-hundred-and-thirty-four thousandths in French but one thousand, two hundred and thirty four in English. If you don't know the source culture (as you commented below), you can't meaningfully determine what was originally meant.
-
JohnG79 over 6 years
Regex.Replace(value, @"(?<=\d)[.,](?=\d)", CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator)
-
-
Andrew Florko over 13 yearsBut I don't know the original culture :(
-
Noon Silk over 13 yearsYou must ensure that you know the original culture, otherwise you can't decide what the number is meant to indicate.
-
Bertvan over 13 yearsWhat if the value is coming from a form? Replace ','s with '.'s before parsing? (edit: okay that was asked not to use, but still...)
-
Kobi over 13 yearsOk, more seriously - you almost never want to use
CurrentCulture
on the server - you don't know how it's set. -
Andrew Florko over 13 yearsHow about InvariantCulture with NumberDecimalSeparator override?
-
Jeff Mercado over 13 years@Kobi: The point was to get an instance of a
CultureInfo
object that could be modifiable without arbitrarily having to choose one. Cloning was necessary since the instance from the static properties were readonly. Though I suppose a clone of theInvariantCulture
would have been a better choice. -
Cheng Chen over 13 years@Bertvan:
CultureInfo.CurrentCulture
provides the default cultureinfo of "your form"(actually it's the cultureinfo of the os) -
Bertvan over 13 years@Danny Chen: Okay, but I'm a Belgian (nl-be - comma decimals) user with an English (en-us - point decimals) OS. While the actual CultureInfo is know, you never know what a user will enter...
-
Evilripper about 7 yearsThe strings are dangerous, possible problem with thousand separator? 1.2345,40?
-
Jiří Herník about 6 yearsand what about the case, where the comma (,) is there only once, but it's still group separating symbol? eg "111,222" meaning 111222.0