Reputation: 245
I want a specific value, the value be only numbers and:
the length should be 11.
the first digit should be 0.
the second digit should be 1.
the third digit should be 0, 1, 2, 5.
then match any digit from the forth digit to the end.
if the third digit is 1, then the last two digits(10th, 11th) should be the same.
if the third digit is 2, the 8th, 9th digits should be the same.
Input string, and expected result.
01012345678 -----> allowed.
0101234a5678 -----> not allowed., letter exists.
01112345688 -----> allowed, 10th, 11st are the same
01112345677 -----> allowed, 10th, 11st are the same
01112345666 -----> allowed, 10th, 11st are the same
01112345689 -----> not allowed..10th, 11st different
01112345-678 -----> not allowed..hyphen exists.
01298765532 -----> allowed..8th, 9th are the same.
01298765732 -----> not allowed, 8th, 9th different.
01298765mm432 -----> not allowed, letter exists.
01500011122 -----> allowed..
020132156456136 -----> not allowed..more than 11 digit.
01530126453333 -----> not allowed..more than 11 digit.
00123456789 -----> not allowed.. second digit not 1.
This is my attempt at regex101,^01[0125][0-9]{8}$
https://regex101.com/r/cIcD0R/1
but it ignore specific cases also it works for specific cases.
Upvotes: 21
Views: 1971
Reputation: 21150
If you're looking for a regex, purely to filter certain numbers without error messaging, this answer is probably not for you.
For validation purposes, a regex might not be the best way to go. If you would use one giant regex you would show one universal error message. This might leave a user confused since they partially complied with some of the criteria.
Instead split up the criteria so you can show a user relevant error messages.
function isValid(input, criteria) {
const errors = [];
for (const [isValid, error] of criteria) {
if (!isValid(input)) errors.push(error);
}
return [!errors.length, errors];
}
const criteria = [
[input => input.length === 11,
"must have a length of 11"],
[input => input.match(/^\d*$/),
"must only contain digits (0-9)"],
[input => input[0] === "0",
"must have 0 as 1st digit"],
[input => input[1] === "1",
"must have 1 as 2nd digit"],
[input => ["0","1","2","5"].includes(input[2]),
"must have 0, 1, 2 or 5 as 3rd digit"],
[input => input[2] !== "1" || input[9] === input[10],
"the 10th and 11th digit must be the same if the 3rd digit is 1"],
[input => input[2] !== "2" || input[7] === input[8],
"the 8th and 9th digit must be the same if the 3rd digit is 2"],
];
document.forms["validate-number"].addEventListener("submit", function (event) {
event.preventDefault();
const form = event.target;
const inputs = form.elements.inputs.value.split("\n");
inputs.forEach(input => console.log(input, ...isValid(input, criteria)));
});
<form id="validate-number">
<textarea name="inputs" rows="14" cols="15">01012345678
0101234a5678
01112345688
01112345677
01112345666
01112345689
01112345-678
01298765532
01298765732
01298765mm432
01500011122
020132156456136
01530126453333
00123456789</textarea>
<br />
<button>validate</button>
</form>
Upvotes: 15
Reputation: 133750
With your shown samples please try following regex. Here is the Online Demo for used regex.
^01(?:(?:[05][0-9]{8})|(?:1[0-9]{6}([0-9])\1)|(?:2[0-9]{4}([0-9])\2[0-9]{2}))$
Here is the JS code for above regex, using foreach
loop along with using test
function in it.
const regex = /^01(?:(?:[05][0-9]{8})|(?:1[0-9]{6}([0-9])\1)|(?:2[0-9]{4}([0-9])\2[0-9]{2}))$/;
[
"01012345678",
"0101234a5678",
"01112345688",
"01112345677",
"01112345666",
"01112345689",
"01112345-678",
"01298765532",
"01298765732",
"01298765mm432",
"01500011122",
"020132156456136",
"01530126453333",
"00123456789"
].forEach(element =>
console.log(`${element} ----> ${regex.test(element)}`)
);
Explanation: Adding detailed explanation for used regex.
^01 ##Matching 01 from starting of the value.
(?: ##Starting outer non-capturing group from here.
(?: ##In a non-capturing group
[05][0-9]{8} ##Matching 0 OR 5 followed by any other 8 digits.
)
| ##Putting OR condition here.
(?: ##In a non-capturing group
1[0-9]{6}([0-9])\1 ##Matching 1 followed by 6 digits followed by single digit(in a capturing group) and making sure next digit is matching previous.
)
| ##Puting OR condition here.
(?: ##In a non-capturing group matching, 2 followed by 4 digits followed by 1 digit in capturing group followed by it followed by 2 any other digits.
2[0-9]{4}([0-9])\2[0-9]{2}
)
)$ ##Closing outer non-capturing grouo here at the last of the value.
Upvotes: 12
Reputation: 163577
You could make use of an alternation with 2 capture groups and backreferences:
^01(?:[05]\d{8}|1\d{6}(\d)\1|2\d{4}(\d)\2\d\d)$
Explanation
^
Start of string01
Match literally(?:
Non capture group for the alternatives
[05]\d{8}
Match either 0
or 5
and 8 digits|
Or1\d{6}(\d)\1
Match 1
, then 6 digits, capture a single digit in group 1 followed by a backreference to match the same digit|
Or2\d{4}(\d)\2\d\d
Match 2
, then 4 digits, capture a single digit in group 2 followed by a backrefence to match the same digit and match the last 2 digits)
Close the non capture group$
End of stringSee a regex101 demo
const regex = /^01(?:[05]\d{8}|1\d{6}(\d)\1|2\d{4}(\d)\2\d\d)$/;
[
"01012345678",
"0101234a5678",
"01112345688",
"01112345677",
"01112345666",
"01112345689",
"01112345-678",
"01298765532",
"01298765732",
"01298765mm432",
"01500011122",
"020132156456136",
"01530126453333",
"00123456789"
].forEach(s => console.log(`${s} => ${regex.test(s)}`))
Upvotes: 27