Problem Parsing Currency Text to Decimal Type

Problem parsing currency text to decimal type

How about using:

decimal d = decimal.Parse("$45.00", NumberStyles.Currency);

The MSDN documentation on Decimal.Parse states:

"The s parameter is interpreted using the NumberStyles.Number style. This means that white space and thousands separators are allowed but currency symbols are not. To explicitly define the elements (such as currency symbols, thousands separators, and white space) that can be present in s, use the Decimal.Parse(String, NumberStyles, IFormatProvider) method

Convert currency string to decimal?

Here is a method that most closely resembles the code you've provided

public static decimal Parse(string input)
{
return decimal.Parse(Regex.Replace(input, @"[^\d.]", ""));
}

Here is an option that will support negative numbers, and will stop if it finds a second period value, thus reducing the number of strings it returns that are not valid decimal values. It also has a few other modifications not seen in the OP to handle additional cases your current code doesn't.

public static decimal Parse(string input)
{
return decimal.Parse(Regex.Match(input, @"-?\d{1,3}(,\d{3})*(\.\d+)?").Value);
}

Generic conversion of numbers taking into account the currency symbol

In my specific scenario I required this to work only with the built-in .Net types. Every numeric type has a Parse method, and as pointed by Stijn in the comments, Parse has an overload that accepts a NumberStyles parameter. Also, it is important to note that Convert.ChangeType works only with a very limited subset of the built-in .Net types, as pointed by Ivan Stoev in the comments. Thanks for the valuable comments!

And now the working solution, I've just made a little tweak into the IsNumber<T> method using some reflection:

public static bool IsNumber<T>(string number, NumberStyles numberStyle) where T : struct ...
try
{
var mi = typeof(T).GetMethod("Parse", new Type[] {typeof(string), typeof(NumberStyles)});
if (mi == null)
return false;
var parsed = mi.Invoke(null, new object[] {number, numberStyle});
return true;
}
catch (...

Method should be called like:

var number = "$1,123.00";
var numberOk = IsNumber<decimal>(number, NumberStyles.Currency);

How can I parse a numeric string with the euro currency symbol?

Change

var cultureInfo = CultureInfo.CreateSpecificCulture("de-DE");

to

var cultureInfo = CultureInfo.CreateSpecificCulture("en-IE");

Output:

5432109.876

Edit:

As mentioned in the comments this works because of the comma digit grouping of the culture. This answer happens to match (so it works), but I agree it's also important to note why this culture works where others don't.

C# string to Decimal On All style or Culture

Well, assuming you always get a valid currency format, and it's only the culture that changes, you could guess which character is used as a decimal point and which is used as a thousands separator by checking which appears the last in the number. Then remove all the thousand separators and parse it like its culture was invariant.

The code would look like the following:

// Replace with your input
var numberString = "2.500,00 €";

// Regex to extract the number part from the string (supports thousands and decimal separators)
// Simple replace of all non numeric and non ',' '.' characters with nothing might suffice as well
// Depends on the input you receive
var regex = new Regex"^[^\\d-]*(-?(?:\\d|(?<=\\d)\\.(?=\\d{3}))+(?:,\\d+)?|-?(?:\\d|(?<=\\d),(?=\\d{3}))+(?:\\.\\d+)?)[^\\d]*$");

char decimalChar;
char thousandsChar;

// Get the numeric part from the string
var numberPart = regex.Match(numberString).Groups[1].Value;

// Try to guess which character is used for decimals and which is used for thousands
if (numberPart.LastIndexOf(',') > numberPart.LastIndexOf('.'))
{
decimalChar = ',';
thousandsChar = '.';
}
else
{
decimalChar = '.';
thousandsChar = ',';
}

// Remove thousands separators as they are not needed for parsing
numberPart = numberPart.Replace(thousandsChar.ToString(), string.Empty);

// Replace decimal separator with the one from InvariantCulture
// This makes sure the decimal parses successfully using InvariantCulture
numberPart = numberPart.Replace(decimalChar.ToString(),
CultureInfo.InvariantCulture.NumberFormat.CurrencyDecimalSeparator);

// Voilá
var result = decimal.Parse(numberPart, NumberStyles.AllowDecimalPoint | NumberStyles.Number, CultureInfo.InvariantCulture);

It does look a bit of complicated for a simple decimal parsing, but I think should do the work for all the input numbers you get or at least the most of them.

If you do this in some sort of loop, you might want to use compiled regex.



Related Topics



Leave a reply



Submit