Michiel Meulendijk
Michiel Meulendijk

Reputation: 365

Regex: limit both the whole match AND character classes within

Is it possible with regex to allow a match to have a maximum number of characters, divided between different character classes?

I need to match a number of up to 4 digits in total, with or without decimal digits. So these should all match the regex:

123
1234
12.34
123.4

But these should not:

12345
12.345

In concept, something like this should work, except it doesn't:

([0-9]{0,4}([.]?[0-9]{0,4})){0,4}

Upvotes: 2

Views: 197

Answers (4)

Toto
Toto

Reputation: 91518

If your regex flavor accept it, you could use lookahead like:

Edit: allow max 2 decimal

^(?:\d{1,4}|(?=.{1,5}$)\d+\.\d{1,2})$

Explanation:

^               : begining of string
  (?:           : start non capture group
    \d{1,4}     : 1 up to 4 digit
    |           : OR
    (?=         : lookahead
      .{1,5}$   : 1 up to 5 character (it could be .{3,5} if at least 1 digit is mandatory on each side of the dot)
    )           : end lookahead
    \d+         : 1 or more digits, integer part
    \.          : dot
    \d{1,2}     : 1 or 2 digits, decimal part
  )             : end group
$               : end of string

var test = [
123,
1234,
12.34,
123.4,
12345,
12.345,
1.234
];
console.log(test.map(function (a) {
  return a+' :'+/^(?:\d{1,4}|(?=.{1,5}$)\d+\.\d{1,2})$/.test(a);
}));

Upvotes: 1

Bohemian
Bohemian

Reputation: 425398

Use a look ahead to assert there's at most 1 dot:

^(?!([^.]\.){2})(?!\d{5})[\d.]{3,5}$
  • (?!([^.]\.){2}) means "looking ahead anywhere, there aren't 2 dots
  • (?!\d{5}) means "looking ahead, there aren't 5 straight digits"
  • [\d.]{3,5} means "3-5 of digits and dots"

See live demo.


To restrict decimal digits to maximum 2, add a (?!.*\.\\d{3,}$) which is a negative look ahead for "dot then 3+ digits at the end", ie:

^(?!([^.]\.){2})(?!\d{5})(?!.*\.\\d{3,}$)[\d.]{3,5}$

See live demo.

Upvotes: 1

m87
m87

Reputation: 4511

The following regex should do it ...

\b(?:\d{1,3}\.\d{1,2}|\d{1}\.\d{1,3}|(?<!\.)\d{1,4}(?!\.))\b

see regex demo / explanation

Upvotes: 1

vallentin
vallentin

Reputation: 26245

It's not pretty, but you can do it like this:

(\d{1,4}|\d{0,3}\.\d|\d{0,2}\.\d{0,2}|\d\.\d{0,3})

Just make sure that you have some boundary control character around it.

Say like this:

(?:^|[^\d.])(\d{1,4}|\d{0,3}\.\d|\d{0,2}\.\d{0,2}|\d\.\d{0,3})(?:$|[^\d.])

You can see here that it works as intended.

I would however advice to use another tool for this specific case.

Upvotes: 1

Related Questions