Is there a cleaner Regex expression for what I have in mind?

Viewed 86

I need to only accept input as numerical values 0 to 100, integers and floats with max two decimal places inclusive, and write a regular expression to check these conditions.

For example, I want it to accept values such as:

0(min), 0.1, 1, 11, 11.1, 11.11, 100(max).

But not any of:

-1, 100.1, 111, 1+1, .1, etc.

So far I came up with ^\d?\d+(\.\d\d?)?$ except that it has a bunch of problems.

Just now while submitting this, I saw this link in similar questions sidebar that has what seems to be the solution ( "^((?:|0|[1-9]\d?|100)(?:\.\d{1,2})?)$" ), except that it also accepts 100.01 to 100.99. Other than that, which is a very minor issue, it should work.

But does anybody know how to patch that particular bit?

4 Answers

100 is the only real exception here, so it should be fairly simple:

^(?:100|\d{1,2}(?:\.\d{1,2})?)$

https://regex101.com/r/Rr0gs4/1

Edit: to also allow 100.0 and 100.00:

^(?:100(?:\.00?)?|\d{1,2}(?:\.\d{1,2})?)$

Depending on how you get the input, it may be easier and quicker to simply check the numerical value.

def acceptable(str_val):
    try:
        return 0 <= float(str_val) <= 100
    except ValueError:
        return False

acceptable('1.11')
# True

acceptable('abc')
# False

acceptable('100.0')
# True

acceptable('100.1')
# False

This tests all the allowable inputs and the example non-allowable inputs.

import re

regex = re.compile(r'^(100(\.00?)?|((\d|[1-9]\d)(\.\d\d?)?))$')

def matches(s):
    print('Testing', s)
    return bool(regex.search(s))

for i in range(0, 101):
    s = str(i)
    assert matches(s), 'no match for %s' % s

for i in range(0, 100):
    for j in range(0, 100):
        s = '{i}.{j}'.format(i=i, j=j)
        assert matches(s), 'no match for %s' % s

        # Special case for .0N (e.g., 1.01, 1.02, etc)
        if j == 0:
            for k in range(0, 10):
                s = '{i}.0{k}'.format(i=i, k=k)
                assert matches(s), 'no match for %s' % s

non_matches = ('-1', '100.1', '111', '1+1', '.1', 'abc')
for s in non_matches:
    assert not matches(s), 'unexpected match for %s' % s

Try this re.match(r'(\d{,2}\.?\d*)?|(100)?', 'string_goes_here')

Related