2

I have looked to multiple SO questions on parsing currency, the best (recommended) way seems to be the one I'm trying below:

var payout = decimal.Parse("$2.10", NumberStyles.Currency | NumberStyles.AllowDecimalPoint);

However, it throws and exception: Input string is not in the correct format.

I don't know what I'm doing wrong?

EDIT

Thanks for the answers. Additional info: the hard-coded currency value I gave was just an example. I have a list of currencies:

€2,66

$2.10

$5.55

etc.

I cannot determine the culture info in advance. Any ideas?

sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
l3utterfly
  • 2,106
  • 4
  • 32
  • 58
  • 3
    Move to another country (one that use $ as currency symbol and `.` as decimal separator) . Alternatively read on `CultureInfo` as it may be cheaper. – Alexei Levenkov Mar 15 '16 at 02:21
  • The currency string will be dynamic, not hard coded like my example. E.g. $2.10, €2,66, etc. How will I know the CultureInfo in advance? – l3utterfly Mar 15 '16 at 02:27
  • Run away... There is no way to parse $3,100 correctly without knowledge of CultureInfo used to format the value. Checking against list of all currency symbols and hoping you've guessed decimal separator correctly is the safest bet (I'd not recommending to take that bet so). – Alexei Levenkov Mar 15 '16 at 02:49

5 Answers5

2

You can try like this:

decimal currencyValue;
string inputCurrency = "$12.6";
if (decimal.TryParse(inputCurrency, NumberStyles.Currency, CultureInfo.CreateSpecificCulture("en-US"), out currencyValue))
  {
      // proceed with currencyValue
  }
else 
  {
      //Show error ; Conversion failed
  }

For dealing with all currencies you can use the following:

        Dictionary<char, string> currencyCulture = new Dictionary<char, string>();
        currencyCulture.Add('$', "en-US");
        currencyCulture.Add('€', "en-IE");
        // populate all posible values here
        decimal currencyValue;
        string inputCurrency = "€2,66";
        char currencySymbol= inputCurrency.ToCharArray()[0];
        CultureInfo currentCulture= CultureInfo.CreateSpecificCulture(currencyCulture[currencySymbol]);
        if (decimal.TryParse(inputCurrency, NumberStyles.Currency, currentCulture, out currencyValue))
        {
            // proceed with currencyValue
        }
        else 
        {
         //Show error ; Conversion failed
        }

You can choose culture Names from here

sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
2

Similar approach @un-lucky mentioned as one of the answer, I tried making it generic and work for every Symbol/Format

public static decimal ParseCurrencyWithSymbol(string input)
{
    var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures)
        .GroupBy(c=> c.NumberFormat.CurrencySymbol)
        .ToDictionary(c=> c.Key, c=>c.First());


    var culture = cultures.FirstOrDefault(c=>input.Contains(c.Key));

    decimal result = 0;
    if(!culture.Equals(default(KeyValuePair<string,CultureInfo>)))
    {
        result = decimal.Parse(input, NumberStyles.Currency | NumberStyles.AllowDecimalPoint, culture.Value);
    }
    else
    {
        if( !decimal.TryParse(input, out result))
        {
            throw new Exception("Invalid number format");
        }
    }

    return result;
}

Usage

decimal output = ParseCurrencyWithSymbol("$2.10");

Working Code

Hari Prasad
  • 16,716
  • 4
  • 21
  • 35
  • With Euros, your code outputs 210, which is wrong, as in EU, the "comma" is the decimal separator, not the "period" – l3utterfly Mar 15 '16 at 03:26
  • @l3utterfly ahh.. not noticed, thanks for pointing. Will fix it. – Hari Prasad Mar 15 '16 at 03:29
  • @l3utterfly fixed it. – Hari Prasad Mar 15 '16 at 03:34
  • Thanks, I used a very similar implementation based on your answer, though I didn't think to use AllCultures and group by currency symbol. Thanks! – l3utterfly Mar 15 '16 at 05:49
  • Still not correct, if you have two same currency symbols, with different decimal point notation, you might use the incorrect notation. A Dutch citizen would write: € 4000,32, while a USA citizen would write € 4,000.32 if he is talking about investing money in the Netherlands – Harald Coppoolse Aug 16 '17 at 09:40
0

How about a CleanCurrency method?

/// Loops each char in the string and returns only numbers, . or ,
static double? CleanCurrency(string currencyStringIn) {
  string temp = "";
  int n;
  for (int i = 0; i < currencyStringIn.Length; i++) {
    string c = currencyStringIn.Substring(i, 1);
    if (int.TryParse(c, out n) || c == "." || c == ",") {
      temp += c;
    }
  }
  if (temp == "") {
    return null;
  else {
    return double.Parse("0" + temp);
  }
}

The idea here being to just get an actual number regardless of what the string content is.

double? payout = CleanCurrency("$3.50");
jleach
  • 7,410
  • 3
  • 33
  • 60
  • What about it? The idea here is "1,500" and "1.500" are very different values - there is no explanation how your code solves that (in addition to some very complicated implementation of `Regex.Replace("$1.2", @"[^\d,\.]","")` ) – Alexei Levenkov Mar 15 '16 at 03:18
0

I've expounded on Hari Prasad's answer. With this you can minimize the culture result. Update "SupportedCultures" with the ones you might use in your app.

    private static readonly List<string> SupportedCultures = new List<string> {"en-US", "en-GB", "fr-FR"};

    public static void Main()
    {
        var (amount, culture) = ParseCurrencyWithSymbol("$256.12");
        Console.WriteLine($"{culture?.Name} | {amount}");

        var (amount2, culture2) = ParseCurrencyWithSymbol("£389.17");
        Console.WriteLine($"{culture2?.Name} | {amount2}");

        var (amount3, culture3) = ParseCurrencyWithSymbol("€421,10");
        Console.WriteLine(culture3 != null ? $"{culture3.Name} | {amount3}" : "Failed!");
    }

    public static Tuple<decimal?, CultureInfo> ParseCurrencyWithSymbol(string input)
    {
        var culture = CultureInfo.GetCultures(CultureTypes.AllCultures)
            .Where(x => SupportedCultures.Contains(x.Name))
            .FirstOrDefault(c => input.Contains(c.NumberFormat.CurrencySymbol));

        if (culture == null) return new Tuple<decimal?, CultureInfo>(null, null);

        return new Tuple<decimal?, CultureInfo>(decimal.Parse(input,
            NumberStyles.Currency | NumberStyles.AllowCurrencySymbol |
            NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands, culture), culture);
    }
-1

Pertinent to your particular case, you may use the following code snippet:

var payout = decimal.Parse("$2.10".Replace("$",""));

If you don't know what the currency symbol would be, then try the following solution:

string _money = "$2.10";
var payout = decimal.Parse(_money.Substring(1));

Dealing with commas and decimal points is much more difficult: if this is the issue, refer to the solution given by member @un-lucky.

Hope this may help.

Alexander Bell
  • 7,842
  • 3
  • 26
  • 42