9

I need to write a regex to allow only positive number ( integers or decimals). I have found this:

/^(?!(?:0|0\.0|0\.00)$)[+]?\d+(\.\d|\.\d[0-9])?$/  

but it just accepts up to 2 decimal places. What changes do I have to make, so that it can accept any number of decimal places?

Also where can I find a good tutorial for learning regex.

Thanks beforehand

icecrime
  • 74,451
  • 13
  • 99
  • 111
eddy
  • 4,373
  • 16
  • 60
  • 94
  • 3
    Is `04` a positive number? Note that that is *not* `04`; the code points differ. Similarly, is `¼` a positive number? What about `3⁴`? I believe you’ll find that `0⁴` is not a positive number, yet `4⁰` actually is. How do you feel about `Ⅷ`, or `ⅽ̄` in lieu of `ↈ`? Isn’t `π` a positive number? Did you know that `⑮` is a positive number, but that `⓯` is a negative number? Have you considered ` `? – tchrist Dec 16 '10 at 23:25
  • The basic test cases need to minimally be +2, 2, -2, 2., .2, 2.2, -2.2, +2.2 — and then again with all those twos turned to zeroes. The answer you accepted fails on some of those. You should probably also add 6e23, 6e-23, and 0e23 to your test cases. And we haven't considered −1, which is an interesting case. – tchrist Dec 16 '10 at 23:48
  • possible duplicate of [Learning Regular Expressions](http://stackoverflow.com/questions/4736/learning-regular-expressions) – hopper Feb 27 '15 at 19:17
  • 1
    This is really not the kind of thing you should be doing with regexes. As the various complicated answers (and their mistakes) illustrate. Think outside the box. Look for an alternative approach. – Stephen C Jul 25 '18 at 23:00

4 Answers4

23

This would be my way: ^[+]?\d+([.]\d+)?$
EDIT: If you want to allow something like .23, you could use ^[+]?([.]\d+|\d+([.]\d+)?)$
EDIT: tchrist insists on this one: allowing 4., you could use ^[+]?([.]\d+|\d+[.]?\d*)$

Explanation:

  • with or without positive sign
  • one or more digits
  • with or without:
    • decimal point
    • one or more digits

Note: This will not accept a negative number, which is what you ultimately want.

BeemerGuy
  • 8,139
  • 2
  • 35
  • 46
  • I've added this (?!(?:0|0\.0|0\.00)$) , to prevent the user from entering 0/0.0/0.00 , but what if it enters 0.000. How can I say "0.any number of zeros" ? – eddy Dec 16 '10 at 22:55
  • 1
    @eddy -- So you don't want to accept a number that ultimately translates to pure 0? – BeemerGuy Dec 16 '10 at 22:58
  • @tchrist -- no it doesn't, the `\d+` will force at least one digit before it gets to the decimal point. – BeemerGuy Dec 16 '10 at 23:10
  • @BeemerGuy: That’s precisely why it fails: because `".23"` is a perfectly valid positive number, but your pattern erroneously rejects it. – tchrist Dec 16 '10 at 23:11
  • @eddy -- I believe the expression would be very complex if you want to account for that in a single shot. You can run it against a second regex to make sure it's not zeros, but that would be an overkill. At that point, programmatically checking if it evaluates to zero is the ultimate solution. Unless there's a regex that can do it that I don't know about; sorry =( – BeemerGuy Dec 16 '10 at 23:13
  • @tchrist -- oh, I thought you meant it will accept it haha. I usually don't like to accept `.23` to force the user to input a `0` to make sure they meant that `.`. But I think I should for eddy's purposes. – BeemerGuy Dec 16 '10 at 23:15
  • Checking the value of the number against zero seems like a job to be done by the program after it interprets or parses the value, this isn't really something regular expressions are good at. – killdash9 Dec 16 '10 at 23:18
  • I believe you’ll find that `4.` is a positive number, one which moreover passes my test but fails yours. :( – tchrist Dec 16 '10 at 23:33
  • @tchrist -- when you say passes or fails, I get confused whether you mean that on the expression or the input value. – BeemerGuy Dec 16 '10 at 23:54
  • @BeemerGuy: I envision a Boolean function `is_positive_number()`; that’s what I mean by passes or fails: whether said function would return true or not. – tchrist Dec 17 '10 at 00:01
  • @tchrist -- in that case, and IMHO, I disagree that `4.` is a valid entry (from a UI-design perspective). This one I will not edit into my answer =). – BeemerGuy Dec 17 '10 at 00:04
  • I don't know a single programming language that rejects `4.` as a numeric literal; indeed, some require it. – tchrist Dec 17 '10 at 00:10
  • @tchrist -- fine, fine, you win! Easy solution =) – BeemerGuy Dec 17 '10 at 00:13
  • @tchrist -- I will not lend you money! =) – BeemerGuy Dec 17 '10 at 00:20
  • If the number is for e.g. -22, it will still match the 22 without the minus, which should not be matched as it is negative – ghoulfolk May 13 '15 at 08:21
  • All good answers here, but if you want a positive number, then you need the `+` so, you have to drop the `?` mark, otherwise it is a non-negative number, which means you could simply not allow `-` as in `^[^-](\d+|\d+\.\d+)$` or if you want stuff like `.23`, then `^[^-](\d+|\d*\.\d+)$` – Viorel Aug 18 '17 at 10:08
9

The short answer is that you need this pattern:

^(?!(?:^[-+]?[0.]+(?:[Ee]|$)))(?!(?:^-))(?:(?:[+-]?)(?=[0123456789.])(?:(?:(?:[0123456789]+)(?:(?:[.])(?:[0123456789]*))?|(?:(?:[.])(?:[0123456789]+))))(?:(?:[Ee])(?:(?:[+-]?)(?:[0123456789]+))|))$

The long answer is contained in the following program:

#!/usr/bin/perl

use strict;
use warnings qw<FATAL all>;

my $number_rx = qr{

  # leading sign, positive or negative
    (?: [+-] ? )

  # mantissa
    (?= [0123456789.] )
    (?:
        # "N" or "N." or "N.N"
        (?:
            (?: [0123456789] +     )
            (?:
                (?: [.] )
                (?: [0123456789] * )
            ) ?
      |
        # ".N", no leading digits
            (?:
                (?: [.] )
                (?: [0123456789] + )
            )
        )
    )

  # abscissa
    (?:
        (?: [Ee] )
        (?:
            (?: [+-] ? )
            (?: [0123456789] + )
        )
        |
    )
}x;

my $negative_rx = qr{ ^ - }x;
my $zero_rx     = qr{ ^ [-+]? [0.]+ (?: [Ee] | $ ) }x;

my $positive_rx = qr{
    ^
    (?!  $zero_rx      )
    (?!  $negative_rx  )
    $number_rx
    $
}x;

my @test_data = qw{
    -2 2 +2 2. -1 1 +1 1.
    0 +0 -0 .0 0.
    1.3 -3.2 5.13.7
    00.00 +00 -00 +0-1
    0000.
    McGillicuddy
    +365.2425
    6.02e23
    .0000000000000000000000000000000000000000000000000000000000000000000
    .00000000000000000000000000000000000000000000000000000000000000000000000001
    .03 0.3 3.0
    0e50 0e-50
    1e50 1e+50 1e-50
};


for my $n (@test_data) {
    printf "%s is%s a positive number.\n",
            $n, $n =~ /$positive_rx/ ? "" : " not";
}

The test results are:

-2 is not a positive number.
2 is a positive number.
+2 is a positive number.
2. is a positive number.
-1 is not a positive number.
1 is a positive number.
+1 is a positive number.
1. is a positive number.
0 is not a positive number.
+0 is not a positive number.
-0 is not a positive number.
.0 is not a positive number.
0. is not a positive number.
1.3 is a positive number.
-3.2 is not a positive number.
5.13.7 is not a positive number.
00.00 is not a positive number.
+00 is not a positive number.
-00 is not a positive number.
+0-1 is not a positive number.
0000. is not a positive number.
McGillicuddy is not a positive number.
+365.2425 is a positive number.
6.02e23 is a positive number.
.0000000000000000000000000000000000000000000000000000000000000000000 is not a positive number.
.00000000000000000000000000000000000000000000000000000000000000000000000001 is a positive number.
.03 is a positive number.
0.3 is a positive number.
3.0 is a positive number.
0e50 is not a positive number.
0e-50 is not a positive number.
1e50 is a positive number.
1e+50 is a positive number.
1e-50 is a positive number.
tchrist
  • 78,834
  • 30
  • 123
  • 180
  • +1 Just curious why, in the abscissa expression, you use an `"OR nothing"` alternative instead of just making that whole thing optional with a `?` i.e. Why `(?:foo|)` vs `(?:foo)?` ? – ridgerunner Mar 24 '11 at 18:10
  • @ridgerunner: I no longer recall, but probably it was because that mapped to how I was thinking about it at the time. Certainly it makes no difference which way it’s written. – tchrist Mar 24 '11 at 20:20
  • Nice! This looks like PCRE. If anyone needs RE2 instead: https://stackoverflow.com/questions/75760026 – system PAUSE Mar 16 '23 at 18:03
2

This should do it.

\+?(\d+(\.(\d+)?)?|\.\d+)

There are tons of regular expression tutorials out there, here is one of them:

http://www.cs.tut.fi/~jkorpela/perl/regexp.html

killdash9
  • 2,314
  • 2
  • 23
  • 17
  • Good catch tchrist, I put in a fix for that. It seems like it should be simpler, but the only way I could do it was to add a second case – killdash9 Dec 16 '10 at 23:14
  • That fails on `100.`, you know. :( – tchrist Dec 16 '10 at 23:34
  • @tchrist it's getting more complicated :) but the new expression should handle that. Seems like it should be simpler at first blush. – killdash9 Dec 16 '10 at 23:39
  • @Russ: these things are always harder than they seem. Look at my test cases. – tchrist Dec 16 '10 at 23:42
  • @Russ: the reasonable way is to use your language’s own numeric comparisons such that you could simply check `x > 0`. Regexes are somewhat ridiculous for value validation of numerics, including netmasks and other ranges. I **can** write a regex that tells you whether something is a prime number, but that doesn’t make it a good idea: `printf "%d is %s\n", $_, (1 x $_) =~ /^(11+)\1+$/ ? "composite" : "prime" for 2..300;`. See? :) – tchrist Dec 17 '10 at 00:10
  • @tchrist -- Agreed, as I said in my other comment, it's not really the job of a regexp to do that type of thing. BTW, that prime number regex is pretty cool. – killdash9 Dec 17 '10 at 00:19
1

This one is kinda simple — /\d*(\.d*)?/g

Update: this one doesn't match empty strings — /(\.)?\d+(\.\d*)?/g
Tested on "-1.5 0 12. -123.4. 15 -2. .4"