Reputation: 123
I think I created a working regular expression for what I need. Just wondering if anyone can break it or see a shorter way to write it.
The regular expression should validate the following...
So here are some examples of valid ones...
9
$9
$0.99
($999,999.99)
(999999)
($999999)
(999,999)
99,999.9
This is what I have come up with:
^\$?(((\d{1,6}(\.\d{1,2})?)|(\d{1,3},\d{3}(\.\d{1,2})?)|\(((\d{1,6}(\.\d{1,2})?)|(\d{1,3},\d{3}(\.\d{1,2})?))\)))$
CORRECTION, my spec was wrong, if the dollar sign is used it must be INSIDE the parenthesis.
Upvotes: 3
Views: 43327
Reputation: 183584
You can express "between one and six digits; comma before the last three digits is optional" a bit more tersely as \d{1,3}(,?\d{3})?
. This also allows you to include only two copies of (\.\d{1,2})?
: one for positive and one for negative, instead of one for positive-without-comma, one for positive-with-comma, etc.
Also, \d{1,2}
can be shortened slightly to \d\d?
, though I'm not sure if that's an improvement.
So, barring some notation like (?(1))
to test if a backreference is set, here's the shortest version I see:
^(\$?\d{1,3}(,?\d{3})?(\.\d\d?)?|\(\$?\d{1,3}(,?\d{3})?(\.\d\d?)?\))$
One perhaps-undesirable aspect of your regex, and of this one, is that they will allow something like $00,012.7
, even though no one uses leading zeroes that way. You can address that by requiring the first digit to be nonzero, and then adding a special case to handle $0
and (0.12)
and so on:
^(\$?(0|[1-9]\d{0,2}(,?\d{3})?)(\.\d\d?)?|\(\$?(0|[1-9]\d{0,2}(,?\d{3})?)(\.\d\d?)?\))$
Edited to add: using a lookahead assertion like F.J suggests in his/her answer, the latter can be shortened to:
^(?!\(.*[^)]$|[^(].*\)$)\(?\$?(0|[1-9]\d{0,2}(,?\d{3})?)(\.\d\d?)?\)?$
Upvotes: 3
Reputation: 208665
Here is one shorter alternative (56 chars to your 114), which will work in almost all regex flavors:
^\$?(?=\(.*\)|[^()]*$)\(?\d{1,3}(,?\d{3})?(\.\d\d?)?\)?$
Example: http://www.rubular.com/r/qtYHEVzVK7
Explanation:
^ # start of string anchor
\$? # optional '$'
(?= # only match if inner regex can match (lookahead)
\(.*\) # both '(' and ')' are present
| # OR
[^()]*$ # niether '(' or ')' are present
) # end of lookaheand
\(? # optional '('
\d{1,3} # match 1 to 3 digits
(,?\d{3})? # optionally match another 3 digits, preceeded by an optional ','
(\.\d\d?)? # optionally match '.' followed by 1 or 2 digits
\)? # optional ')'
$ # end of string anchor
Upvotes: 12
Reputation: 2621
Given your examples, the following regular expression will work:
/^(\$?(?(?=\()(\())\d+(?:,\d+)?(?:\.\d+)?(?(2)\)))$/gm
(note: flags and delimiters are language dependent)
This regex sets an unnecessary backreference merely to save regex-length. You can ignore the second backreference. If this is intolerable the expression will become quite a bit longer.
Have a look here: http://regex101.com/r/fH3lV1
Upvotes: 3