9

I need to validate if a number is valid with using decimal separator and thousand separators optionally. I need a regex, because the current code, that is not done by me uses a regex that is not working properly and I am aiming to fix it.

So the following combinations would be legal (Using . as decimal separator and , as thousand separator)

  • 10000
  • 100,000,000
  • 100,000,000.345345
  • 10000.689

But the following wouldn't be legal:

  • 10000,000,000.34534

Basically I want numbers with a proper use of decimal and thousand separator or without them at all, but not invalid combinations. I looked around a lot, so far I found this post. Which has a great regex that matches perfectly when the numbers have decimal separator and thousand separator, but when I try to add the other options (numbers only with decimal separator, or number integers) I break the expression.

I decided that maybe I could capture groups matching the rules with or, so the number matches one expression or the other like this:

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

The first group is the expression from the other post, the second I think covers numbers with a decimal separator and the last one are integers, but it is not working.

For example these numbers are not getting matched:

  • 10000000.56
  • 10000000

I think the issue it is happening because it always test for the first group, but I am not sure how to use the groups properly so it matches either of them. Also I think I am placing the -+ signs in the wrong place.

I did search a lot, and couldn't find one expression that worked properly. From the ones suggested:

From the Regex for number with decimals and thousand separator post

  • ((\d){1,3})+([,][\d]{3})*([.](\d)*)? -> It wrongly captures 10000000. and 10,000,000.
  • ^(?!0+\.00)(?=.{1,9}(\.|$))\d{1,3}(,\d{3})*(\.\d+)?$ -> it fails for 10000000
  • /^(?!0+\.00)(?=.{1,9}(\.|$))(?!0(?!\.))\d{1,3}(,\d{3})*(\.\d+)?$/ -> it fails for 10000000
  • ((\d){1,3})+([,][\d]{3})*([.](\d)*)? -> It fails for -100.03

This Can I use an OR in regex without capturing what's enclosed? post is not a duplicate, it explains something that could help me solve the issue, but it is not a duplicate, especially because I have issues also with the +- signs. Also my questions covers a problem that is not particular to me, this is a problem many people face, yet from the implementations I have found so far, none work.

From RegEx matching numeric values with or without thousand separators post

  • ^-?([0-9]{1,3}\.?)+$ -> It fails for -1000 but not for and not for 100.00.3
  • ^-?(?!0)(?:\d+|\d{1,3}(?:\.\d{3})+)$ -> It fails for -100.03
Jason Aller
  • 3,391
  • 28
  • 37
  • 36
Dzyann
  • 4,570
  • 10
  • 51
  • 86
  • Possible duplicate of [Validate decimal numbers in JavaScript - IsNumeric()](https://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric) –  Dec 18 '18 at 17:51
  • 1
    [This is being discussed on meta](https://meta.stackoverflow.com/questions/378008/should-i-try-to-get-my-question-reopened?noredirect=1#comment657168_378008) –  Dec 18 '18 at 17:53
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/185445/discussion-between-dzyann-and-jarrod-roberson). – Dzyann Dec 18 '18 at 20:06
  • 6
    @JarrodRoberson Suggesting an alternative approach is a good part of any answer, but that doesn't invalidate the question. People need to stop invoking "XY" as a reason to completely dismiss not just a perfectly valid technical question about a thing in a feature, but also the person asking it. Please remember to _be nice_, and remember that you don't have to answer the question if you don't like it. – Lightness Races in Orbit Dec 19 '18 at 12:30

2 Answers2

14

The reason the second alternative isn't matching is because it only allows a single \f after the decimal point. That needs to be \d+.

Then you need to wrap everything between ^ and $ in a group, so all alternatives match the entire string.

You had lots of redundant parentheses. And \d* in the last alternative should be \d+, otherwise you'll allow a number that's completely empty or just a sign.

^[+-]?([0-9]{1,3}(,[0-9]{3})*(\.[0-9]+)?|\d*\.\d+|\d+)$
  • ^ -> start of string
  • [+-]? -> matches optional + or - char
  • ([0-9]{1,3}(,[0-9]{3})*(\.[0-9]+)?|\d*\.\d+|\d+) -> whole group has to match [0-9]{1,3}(,[0-9]{3})*(\.[0-9]+) or \d*\.\d+ or \d+
    • [0-9]{1,3}(,[0-9]{3})*(\.[0-9]+) -> matches numbers with thousand separators and maybe decimal separator
    • \d*\.\d+ -> matches numbers with decimal separator, and maybe digits before the decimal
    • \d+ -> matches numbers without decimal separator
  • $ -> end of string

DEMO

Barmar
  • 596,455
  • 48
  • 393
  • 495
  • Thanks for your answer, your expression works almost, but when I use a number like -1000003.65, it fails, but it works for -1,000,003.65 – Dzyann Dec 18 '18 at 14:15
  • `[+-]?` was only in the first alternative, it needs to be outside the group of alternatives so you can put a sign before any format. – Barmar Dec 18 '18 at 16:46
  • I didnt want to bother you, so I edited your question with a slight change. Your regex was matching ".7" as valid, It is because the (\d)*) I changed it to (\d)+). Then I added a brief break down of what the parts do, to see if I am getting it right. If you dont like it please feel free to rollback. – Dzyann Dec 18 '18 at 17:14
  • Let me know if you agree with my changes or if you want to make an update (I didnt update your demo link) and I will accept your answer. – Dzyann Dec 18 '18 at 17:32
  • I thought `.7` should be valid, we sometimes write fractions without putting anything before the `.` – Barmar Dec 18 '18 at 17:34
  • I agree, I didn't clarify that on my quesiton, either way works great. Thanks! – Dzyann Dec 18 '18 at 17:35
  • There's probably some redundant parentheses, like `((\d)+)+?` could just be `\d+`. It doesn't matter whether it's greedy or not in this case. – Barmar Dec 18 '18 at 17:36
  • The parenthesis in regex always get me. If you feel there are too many please update it, I just changed your regex the least I could in order to get the desired outcome. I didn't want to mess too much with your answer. But please feel free to update it as you see fit. – Dzyann Dec 18 '18 at 17:38
  • In regex, parentheses are for capturing. If you use parentheses just to help you applying a `*`, a `+`, a `?` or a `{x}`, then it's better to make it non-capturing with the `(?:)` syntax. – Cœur Dec 20 '18 at 06:59
  • @Cœur That's true, but I don't follow it religiously. If you're just using the regexp to test a string, rather than using the capture groups for anything (e.g. replacement), the extra captures are effectively ignored. There's performance overhead to save them, but it's usually insignificant. The `(?:)` syntax is more verbose and makes the regexp harder to read IMHO. – Barmar Dec 20 '18 at 17:47
  • @Barmar that's where I wish for some new regex syntax where the default would be non-capturing, and where captures would need to be explicit. But then we would lose the C from PCRE, so it won't happen soon. – Cœur Dec 21 '18 at 00:57
6

You may use this regex for validation:

^[+-]?(?:\d+|\d{1,3}(?:,\d{3})*)(?:\.\d*)?$

RegEx Demo

It matches following cases:

  • An integer number
  • A floating point number
  • A number with separators as , at 3rd place
anubhava
  • 664,788
  • 59
  • 469
  • 547
  • 2
    No, it doesn't. It won't work for `100,000,000`, for example, as evidenced by your demo, and fails to validate *loads* of other numbers. This isn't useful. – IInspectable Dec 18 '18 at 22:37
  • 3
    Yes, my bad, I made a minor change in the end that broke it. I have fixed it now in answer and in demo. This regex is still shorter & faster, that would cover all the cases mentioned in OP. If there is something not working, please let me know. – anubhava Dec 19 '18 at 03:44
  • 1
    If you also want to disallows leading `0` and trailing `.` you can use this expression: `^[+-]?(0|[1-9]\d{0,2}((\,\d{3})+|\d*))(\.\d+)?$` https://regex101.com/r/GPICKH/3 – Håken Lid Dec 20 '18 at 14:17