Split string into decimal using any decimal separator
I want to parse a string from text input to decimal. The value represents the currency.
I currently got this solution:
private Decimal CastToDecimal(string value)
{
Decimal result;
var valid = Decimal.TryParse(value, NumberStyles.Currency, null, out result);
return valid ? result : -1;
}
It works very well so far, barring possible cultural differences. I am German and I expect most users to login in Francs style. But it is possible that someone is using "." instead of ",", and the conversion will fail.
"β¬ 123.45" => 123.45
"123.456.78 β¬" => 123456.78
"123.45 β¬" => 12345 <- I want the result to be 123.45 here
Is there a way to automatically detect the culture used for the decimal value? So it doesn't matter if you use German or English punctuation, will you still get the same result?
Update:
Thanks to your help, I have created a method that does what I want (I think).
private static Decimal CastToDecimal(string value)
{
Decimal resultDe;
Decimal resultEn;
var style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands;
var cultureDe = CultureInfo.CreateSpecificCulture("de-DE");
var cultureEn = CultureInfo.CreateSpecificCulture("en-GB");
var deValid = Decimal.TryParse(value, style, cultureDe, out resultDe);
var enValid = Decimal.TryParse(value, style, cultureEn, out resultEn);
var minVal = Math.Min(resultDe, resultEn);
var maxVal = Math.Max(resultDe, resultEn);
if (!deValid)
return resultEn;
if (!enValid)
return resultDe;
return BitConverter.GetBytes(decimal.GetBits(minVal)[3])[2] > 2 ? maxVal : minVal;
}
This code ...
Console.WriteLine(CastToDecimal("123,45"));
Console.WriteLine(CastToDecimal("123.45"));
Console.WriteLine(CastToDecimal("123,450"));
Console.WriteLine(CastToDecimal("123.450"));
Console.WriteLine(CastToDecimal("123.123,45"));
Console.WriteLine(CastToDecimal("123,123.45"));
returns this:
123,45 123,45 123450 123450 123123,45 123123,45
source to share
the solution at http://msdn.microsoft.com/en-us/library/3s27fasw%28v=vs.110%29.aspx which includes installing NumberStyle might be helpful.
...
value = "1.345,978";
style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands;
culture = CultureInfo.CreateSpecificCulture("es-ES");
if (Double.TryParse(value, style, culture, out number))
Console.WriteLine("Converted '{0}' to {1}.", value, number);
else
Console.WriteLine("Unable to convert '{0}'.", value);
// Displays:
// Converted '1.345,978' to 1345.978.
value = "1 345,978";
if (Double.TryParse(value, style, culture, out number))
Console.WriteLine("Converted '{0}' to {1}.", value, number);
else
Console.WriteLine("Unable to convert '{0}'.", value);
...
source to share
I ran into the same problem a while ago. My solution was to write my own parser in Java. The algorithm first clears the string. Below is a brief description:
- Scan line from left to right
- If char = '.' then dotFound = true; lastSeparatorPosition = index; dots ++
- If char = ',' then commaFound = true; lastSeparatorPosition = index; commas ++
- If dots == 0 && commas == 0 then its integer => done
- If dots> 0 && commas> 0, then one of them by default SeparatorPosition is the decimal separator. Remove the rest from the line => done
- / * only one type of separator * / if (periods + commas)> 1, then remove them // because there must be a thousand separator => done
- / * the separator happens once * / if numberOfDigits to the right of the separator == 3, then you have to decide :-) either an integer or a decimal with 3 digits in a fraction
7 is the only remaining problem, as already mentioned, about maintaining security. Here you can only take into account the conceptual environment. All other cases are safe.
Good luck
source to share
The only solution is to add validation to the input and give the user and an example, by the way, if it's a web page, and then find a way to enter the data according to the user's culture. I suggest that you do not try to do what you are trying, because there is some culture that contradicts each other, for example in currency;
US / Australia / Many others use the following format
45,999.95
where, there are thousand separators and. is the decimal separator
whereas in some European countries
45.999,95
means the same as above, but the thousands separator. and is used as a decimal separator.
Now the problem is that there is no guarantee that the user is using both the separator and your system can accept the thousands separator as decimal, etc.
If you really don't want to disturb the user, then create separate input fields for the major and minor currencies.
So it's best not to go there. I believe this might help. Happy coding :)
Update
Same case with date format, for example. in US format, month comes first, followed by day, while Australia day comes first, and then month; now 02/01/2015
typing would mean that the system cannot tell the user's intent differently.
source to share