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
What Advantages of Extension Methods Have You Found
Differencebetween Preservereferenceshandling and Referenceloophandling in JSON.Net
Programmatically Set Browser Proxy Settings in C#
Authorization Header Is Lost on Redirect
Registering a Custom JSONconverter Globally in JSON.Net
How to Handle JSON That Returns Both a String and a String Array
C# Naming Convention for Constants
How to Read Attribute Value from Xmlnode in C#
How to Use JSON.Net for JSON Modelbinding in an MVC5 Project
How to Add Claims in ASP.NET Identity
C# Console Receive Input with Pipe
How to Get a List of All Routes in ASP.NET Core
Why Does (Int)(Object)10M Throw "Specified Cast Is Not Valid" Exception
Double Buffering When Not Drawing in Onpaint(): Why Doesn't It Work
404 Error After Adding Web API to an Existing MVC Web Application