Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Regex to match exactly 5 numbers and one optional space

Tags:

I recently needed to create a regular expression to check input in JavaScript. The input could be 5 or 6 characters long and had to contain exactly 5 numbers and one optional space, which could be anywhere in the string. I am not regex-savvy at all and even though I tried looking for a better way, I ended up with this:

(^\d{5}$)|(^ \d{5}$)|(^\d{5} $)|(^\d{1} \d{4}$)|(^\d{2} \d{3}$)|(^\d{3} \d{2}$)|(^\d{4} \d{1}$)   

This does what I need, so the allowed inputs are (if 0 is any number)

'00000'   ' 00000'   '0 0000'   '00 000'   '000 00'   '0000 0'   '00000 ' 

I doubt that this is the only way to achieve such matching with regex, but I haven't found a way to do it in a cleaner way. So my question is, how can this be written better?

Thank you.

Edit:
So, it is possible! Tom Lord's answer does what I needed with regular expressions, so I marked it as a correct answer to my question.

However, soon after I posted this question, I realized that I wasn't thinking right, since every other input in the project was easily 'validatable' with regex, I was immediately assuming I could validate this one with it as well.

Turns out I could just do this:

const validate = function(value) {     const v = value.replace(/\s/g, '')     const regex = new RegExp('^\\d{5}$');     return regex.test(v); }   

Thank you all for the cool answers and ideas! :)

Edit2: I forgot to mention a possibly quite important detail, which is that the input is limited, so the user can only enter up to 6 characters. My apologies.

like image 944
EyfI Avatar asked Jun 09 '17 16:06

EyfI


People also ask

How does regex Match 5 digits?

match(/(\d{5})/g);

How do I match a character except space in regex?

You can match a space character with just the space character; [^ ] matches anything but a space character.

How do you specify a space in regex?

Find Whitespace Using Regular Expressions in Java The most common regex character to find whitespaces are \s and \s+ . The difference between these regex characters is that \s represents a single whitespace character while \s+ represents multiple whitespaces in a string.

Which regex matches one or more digits?

Occurrence Indicators (or Repetition Operators): +: one or more ( 1+ ), e.g., [0-9]+ matches one or more digits such as '123' , '000' . *: zero or more ( 0+ ), e.g., [0-9]* matches zero or more digits. It accepts all those in [0-9]+ plus the empty string.


2 Answers

Note: Using a regular expression to solve this problem might not be the best answer. As answered below, it may be easier to just count the digits and spaces with a simple function!

However, since the question was asking for a regex answer, and in some scenarios you may be forced to solve this with a regex (e.g. if you're tied down to a certain library's implementation), the following answer may be helpful:

This regex matches lines containing exactly 5 digits:

^(?=(\D*\d){5}\D*$) 

This regex matches lines containing one optional space:

^(?=[^ ]* ?[^ ]*$) 

If we put them together, and also ensure that the string contains only digits and spaces ([\d ]*$), we get:

^(?=(\D*\d){5}\D*$)(?=[^ ]* ?[^ ]*$)[\d ]*$ 

You could also use [\d ]{5,6} instead of [\d ]* on the end of that pattern, to the same effect.

Demo

Explanation:

This regular expression is using lookaheads. These are zero-width pattern matchers, which means both parts of the pattern are "anchored" to the start of the string.

  • \d means "any digit", and \D means "any non-digit".

  • means "space", and [^ ] means "any non-space".

  • The \D*\d is being repeated 5 times, to ensure exactly 5 digits are in the string.

Here is a visualisation of the regex in action:

regex visualisation

Note that if you actually wanted the "optional space" to include things like tabs, then you could instead use \s and \S.


Update: Since this question appears to have gotten quite a bit of traction, I wanted to clarify something about this answer.

There are several "simpler" variant solutions to my answer above, such as:

// Only look for digits and spaces, not "non-digits" and "non-spaces": ^(?=( ?\d){5} *$)(?=\d* ?\d*$)  // Like above, but also simplifying the second lookahead: ^(?=( ?\d){5} *$)\d* ?\d*  // Or even splitting it into two, simpler, problems with an "or" operator:  ^(?:\d{5}|(?=\d* \d*$).{6})$ 

Demos of each line above: 1 2 3

Or even, if we can assume that the string is no more than 6 characters then even just this is sufficient:

^(?:\d{5}|\d* \d*)$ 

So with that in mind, why might you want to use the original solution, for similar problems? Because it's generic. Look again at my original answer, re-written with free-spacing:

^ (?=(\D*\d){5}\D*$) # Must contain exactly 5 digits (?=[^ ]* ?[^ ]*$)  # Must contain 0 or 1 spaces [\d ]*$            # Must contain ONLY digits and spaces 

This pattern of using successive look-aheads can be used in various scenarios, to write patterns that are highly structured and (perhaps surprisingly) easy to extend.

For example, suppose the rules changed and you now wanted to match 2-3 spaces, 1 . and any number of hyphens. It's actually very easy to update the regex:

^ (?=(\D*\d){5}\D*$)       # Must contain exactly 5 digits (?=([^ ]* ){2,3}[^ ]*$)  # Must contain 2 or 3 spaces (?=[^.]*\.[^.]*$)        # Must contain 1 period [\d .-]*$   # Must contain ONLY digits, spaces, periods and hyphens 

...So in summary, there are "simpler" regex solutions, and quite possibly a better non-regex solution to OP's specific problem. But what I have provided is a generic, extensible design pattern for matching patterns of this nature.

like image 70
Tom Lord Avatar answered Oct 14 '22 03:10

Tom Lord


I suggest to first check for exactly five numbers ^\d{5}$ OR look ahead for a single space between numbers ^(?=\d* \d*$) among six characters .{6}$.

Combining those partial expressions yields ^\d{5}$|^(?=\d* \d*$).{6}$:

let regex = /^\d{5}$|^(?=\d* \d*$).{6}$/;    console.log(regex.test('00000'));   // true  console.log(regex.test(' 00000'));  // true  console.log(regex.test('00000 '));  // true  console.log(regex.test('00 000'));  // true  console.log(regex.test('  00000')); // false  console.log(regex.test('00000  ')); // false  console.log(regex.test('00  000')); // false  console.log(regex.test('00 0 00')); // false  console.log(regex.test('000 000')); // false  console.log(regex.test('0000'));    // false  console.log(regex.test('000000'));  // false  console.log(regex.test('000 0'));   // false  console.log(regex.test('000 0x'));  // false  console.log(regex.test('0000x0'));  // false  console.log(regex.test('x00000'));  // false

Alternatively match the partial expressions separately via e.g.:

/^\d{5}$/.test(input) || input.length == 6 && /^\d* \d*$/.test(input) 
like image 39
le_m Avatar answered Oct 14 '22 03:10

le_m