UberNubIsTrue
UberNubIsTrue

Reputation: 642

Regular Expression for Currency (no dollar sign, has comma, has decimal, optional parentheses for negatives)

I apologize if this post is repetitive but I've searched extensively and can't seem to find a solution that works.

I am working on a regular expression for C#. Here are the criteria to match:

A couple of examples:

  1,024.12
    500.00
 10,456.23
      2.89
 (8,456.15)
     (1.63)

This is the current pattern I am using:

^\(?\d+\,?\d+[.]{1}\d{2}\)?

I think this pattern would do the trick, however, there is a string built out of numbers and periods on the same line that matches this pattern. Example:

123.1234.12345.123456789.12345.123 

I know this is pretty basic stuff but I'm having trouble getting it to only match the currency portion and not the string above.

Thanks for any suggestions you can provide

Upvotes: 1

Views: 5131

Answers (6)

Austin Salonen
Austin Salonen

Reputation: 50225

If the goal is just to parse a decimal, decimal.Parse or decimal.TryParse with a properly set NumberStyles will do what you need.

const NumberStyles numberStyles = NumberStyles.AllowParentheses
                                    | NumberStyles.AllowThousands
                                    | NumberStyles.AllowDecimalPoint;
decimal parsed;
var successful = decimal.TryParse(s, numberStyles, CultureInfo.CurrentCulture, out parsed);

Complete test:

var values = new[]
    {
        "1,024.12",
        "500.00", 
        "10,456.23", 
        "2.89", 
        "(8,456.15)", 
        "(1.63)", 
        "123.1234.12345.123456789.12345.123"
    };

const NumberStyles numberStyles = NumberStyles.AllowParentheses
                                    | NumberStyles.AllowThousands
                                    | NumberStyles.AllowDecimalPoint;
var culture = CultureInfo.CurrentCulture;

foreach (var s in values)
{
    decimal parsed;
    var successful = decimal.TryParse(s, numberStyles, culture, out parsed);

    Console.WriteLine("Successful? {0}; Parsed = {1}", 
        successful, 
        successful ? parsed.ToString() : "?");
}

Output:

Successful? True; Parsed = 1024.12
Successful? True; Parsed = 500.00
Successful? True; Parsed = 10456.23
Successful? True; Parsed = 2.89
Successful? True; Parsed = -8456.15
Successful? True; Parsed = -1.63
Successful? False; Parsed = ?

Upvotes: 1

Smern
Smern

Reputation: 19076

I think this would be a good solution for you:

^\(?(0|[1-9][0-9]{0,2}(?:(,[0-9]{3})*|[0-9]*))\.[0-9]{2}\)?$

This will prevent numbers that don't make much sense such as 004,2.42

enter image description here

Upvotes: 1

blsmfrth
blsmfrth

Reputation: 9

You should try

^\(?\d+\,?\d+[.]{1}\d{2}\)?\\> 

or

^\(?\d+\,?\d+[.]{1}\d{2}\)?\b

so that it doesn't match on partial hits.

Upvotes: 0

Ibrahim Najjar
Ibrahim Najjar

Reputation: 19423

Your current expression won't match the fourth 2.89 and the sixth example (1.63) in your list.

Instead you can use the following expression:

^(\()?[0-9]+(?>,[0-9]{3})*(?>\.[0-9]{2})?(?(1)\))$

This expression matches start-of-line ^, then it tries to match an optional parentheses ( for negative numbers, the parentheses is captured in a group for a reason that will be cleared soon.

Now it tries to match one or more numbers [0-9]+, this should cover all whole numbers like 234, 5652, etc ..

Then it looks for a possible comma (thousands separator) , followed by 3 digits (?>,[0-9]{3})* repeated zero or more times, this covers numbers that contain thousand separators.

Then it tries to find a decimal point . followed by exactly 2 digits (?>\.[0-9]{2})? which is of course optional and this covers decimal numbers.

After that, the expression uses (?(1)\)) - the conditional construct (?(id/name)yes-pattern|no-pattern) - so that if we actually matched an open parentheses then we should match a closing one, this prevents the expression from matching incorrect negative numbers such as (2.4 with no closing parentheses.

Finally comes the end-of the-string $ to prevent partial matches such as your last example.

Regex 101 Demo

Upvotes: 5

David Brabant
David Brabant

Reputation: 43499

\(?\b[0-9]{1,3}(?:,?[0-9]{3})*\.[0-9]{2}\b\)?

Hover over the expression here to have an explanation.

Upvotes: 3

Vince
Vince

Reputation: 1527

Try adding $ to the end of your regex, so you'll get

^\(?\d+\,?\d+[.]{1}\d{2}\)?$

That way you will at least only match numbers with exactly 2 digits after the . and the string you mentioned, shouldn't match anymore.

Upvotes: 3

Related Questions