Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I'm working on constructing a PHP regex expression for a username validation that has the following constraints:
-must be 10-16 characters long, with a combo of alphabetic, numeric, and atleast one special character (*&^-_$)
-can't start with a numeric or special character
CORRECTION: the last SIX digits must be a month/date birthday (MMYYYY format). In order to further validate the username, the month/date must show the username is over 18 - if not, the username will not validate. Thank you in advance for any assistance! I've been stuck on this for a while.
Solution
You can do this with the following regex:
/(?=^.{10,16}$)(?=.+?[*&^_$-])[a-z].+?[01]\d{3}$/i
Here's a demo with some unit tests.
Explanation
/ delimiter
(?=^.{10,16}$) ensures there are 10-16 characters, start to finish
(?= starts a lookahead group
^ start of the string
.{10,16} ten to sixteen characters
$ end of the string
) ends the lookahead group
(?=.+?[*&^_$-]) ensures there is at least one special character in the set *&^_$-, and it's not first
(?= starts a lookahead group
.+? one or more characters, non-greedy
[*&^_$-] any character in the set *&^_$- (note the order; you must put - first or last, or escape it as \-)
) ends the lookahead group
[a-z] start with a letter
.+? match any characters in a non-greedy fashion, giving back as needed
[01]\d{3} match a 0 or 1 then 3 more digits
$ match the end of the string
/ closing delimiter
i make the match case-insensitive
Some Notes on Regex Construction
Note that there are multiple valid ways to do this. For pure efficiency, the solution above could be simplified somewhat to cut out a few steps for the processor.
But for readability, I like to go with something like the above. It's clear what each block, character set, or group does, which makes it readable and maintainable.
Something like /^[a-z](?=.*?[*&^_$-])[a-z0-9*&^_$-]{5,11}[01]\d{3}$ is hard to read and understand. What if you want to allow a 17 character username? You have to do a bunch of math to determine that you should change {5,11} to {5,12}. Or if you decide to allow the character #, you'd have to add it in two places (which, by the way, means that the regex already violates the DRY principle).
Bonus: Why Your Attempt Failed
You said in a comment that you tried this:
(?=^.{10,16}$)^[a-z][\d]*[_$^&*]?[a-z0-9]+
The first part, (?=^.{10,16}$), is fine. So is ^[a-z].
But [\d]* only matches zero or more digits; it wouldn't match a letter or special character. So, for example, a&a... would fail.
And [_$^&*]? only matches zero or one special characters. It would allow a username with no special characters to pass, but would fail one with 2 special characters.
[a-z0-9]+ only matches those characters, and you omit your last-four-characters-must-be-digits requirement.
You might find the explanation on regex101.com of your regex helpful. (Note: I have no affiliation with that site.)
You can use this regex :
^[a-zA-Z](?=.*[*&^_$-])[\w*&^$-]{5,11}[01]\d{3}$
Regex Breakup:
^ # Line start
[a-zA-Z] # # match an alhpabet
(?=.*[*&^_$-]) # lookahead to ensure there is at least one special char
[\w*&^$-]{5,11} # match 5 to 11 of allowed chars
[01]\d{3} # match digits 0/1 followed by 3 digits
$ # Line end
I used quantifier {5,11} because one char is matches at start and 4 are being matched in the end thus taking out 5 positions from desired {10,16} lengths.
Related
Have read the following which have some overlap (pun intended!) with the issue I am facing:
preg_match_all how to get *all* combinations? Even overlapping ones
Overlapping matches with preg_match_all and pattern ending with repeated character
However, I don’t really know how to apply their answers to my issue which is a little more complicated.
My regex that I use with preg_match_all():
/.{240}[^\[]Order[^ ][^\(].{9}/u
With the following string:
56A. Subject to the provisions of this Act, any decision of the Court or the Appeal Board shall be final and conclusive, and no decision or order of the Court or the Appeal Board shall be challenged, appealed against, reviewed, quashed or called into question in any court and shall not be subject to any Quashing Order, Prohibiting Order, Mandatory Order or injunction in any court on any account.[20/99; 42/2005]
I intended it to match exactly 3 times. The first match has “Quashing Order” 9 characters before the end. The second match has “Prohibiting Order” 9 characters before the end. The third match has “Mandatory Order” 9 characters before the end.
However, as expected it’s only matching the first one, as the expected matches are overlapping.
I applied what I read in the other posts, I tried this:
(?=(.{240}[^\[]Order[^ ][^\(].{9}))
I still don’t get what I need.
How do I solve this?
You can use
\w+\s+Order\b
See the regex demo.
Regex details
\w+ - one or more word chars
\s+ - 1 or more whitespaces
Order\b - a whole word Order, as \b is a word boundary.
You will need to use a positive look-behind assertion for .{240}, just like the answer you found suggests using a positive look-ahead assertion for .{9}:
/(?<=.{240})[^\[]Order[^ ][^\(](?=.{9})/u
This RE matches your string only twice because of [^ ], as #bobblebubble said. Adjust that part as necessary.
Im creating a regex that searches for a text, but only if there isnt a dash after the match. Im using lookahead for this:
Regex: Text[\s\.][0-9]*(?!-)
Expected result Result
--------------- -------
Text 11 Text 11 Text 11
Text 52- <No Match> Text 5
Test case: https://regex101.com/r/doklxc/1/
The lookahead only seems to be matching with the previous character, which leaves me with Text 5, while I need it to not return a match at all.
Im checking the https://www.regular-expressions.info/ guides and tried using groups, but I cant wrap my head around this one.
How can I make it so the lookbehind function affects the entire preceding match?
Im using the default .Net Text.RegularExpressions library.
The [0-9]* backtracks and lets the regex engine find a match even if there is a -.
There are two ways: either use atomic groups or check for a digit in the lookahead:
Text[\s.][0-9]*(?![-\d])
Or
Text(?>[\s.][0-9]*)(?!-)
See the regex demo #1 and the regex demo #2.
Details
Text[\s.][0-9]*(?![-\d]) matches Text, then a dot or a whitespace, then 0 or more digits, and then it checks of there is a - or digit immediately to the right, and if there is, it fails the match. Even when trying to backtrack and match fewer digits than it grabbed before, the \d in the lookahead will fail those attempts
Text(?>[\s.][0-9]*)(?!-) matches Text, then an atomic group starts where backtracking won't be let in after the group patterns find their matching text. (?!-) only checks for a - after the [0-9]* pattern tries to grab any digits.
Assuming I have a set of numbers (from 1 to 22) divided by some trivial delimiters (comma, point, space, etc). I need to make sure that this set of numbers does not contain any repetition of the same number. Examples:
1,14,22,3 // good
1,12,12,3 // not good
Is it possible to do via regular expression?
I know it's easy to do using just php, but I really wander how to make it work with regex.
Yes, you could achieve this through regex via negative looahead.
^(?!.*\b(\d+)\b.*\b\1\b)\d+(?:,\d+)+$
(?!.*\b(\d+)\b.*\b\1\b) Negative lookahead at the start asserts that the there wouldn't be a repeated number present in the match. \b(\d+)\b.*\b\1\b matches the repeated number.
\d+ matches one or more digits.
(?:,\d+)+ One or more occurances of , , one or more digits.
$ Asserts that we are at the end .
DEMO
OR
Regex for the numbers separated by space, dot, comma as delimiters.
^(?!.*\b(\d+)\b.*\b\1\b)\d+(?:([.\s,])\d+)(?:\2\d+)*$
(?:([.\s,])\d+) capturing group inside this non-capturing group helps us to check for following delimiters are of the same type. ie, the above regex won't match the strings like 2,3 5.6
DEMO
You can use this regex:
^(?!.*?(\b\d+)\W+\1\b)\d+(\W+\d+)*$
Negative lookahead (?!.*?(\b\d+)\W+\1\b) avoids the match when 2 similar numbers appear one after another separated by 1 or more non-word characters.
RegEx Demo
Here is the solution that fit my current need:
^(?>(?!\2\b|\3\b)(1\d{1}|2[0-2]{1}|\d{1}+)[,.; ]+)(?>(?!\1\b|\3\b)(1\d{1}|2[0-2]{1}|\d{1}+)[,.; ]+)(?>(?!\1\b|\2\b)(1\d{1}|2[0-2]{1}|\d{1}+))$
It returns all the sequences with unique numbers divided by one or more separator and also limit the number itself from 1 to 22, allowing only 3 numbers in the sequence.
See working example
Yet, it's not perfect, but work fine! Thanks a lot to everyone who gave me a hand on this!
I am trying to write a regular expression to allow numbers and only one hypen in the middle (cannot be at start or at the end)
say pattern: 02-04 , 02are acceptable but
pattern: -- or - or -02 or 04- or 02-04-06 are unacceptable
I tried something like this but this would allow - at the beginning and also allow multiple -
'/^[0-9 \-]+$/'
I am not that good with regex so a little explanation would be real helpful.
EDIT: Sorry to bug you again with this but I need the numbers to be of only 2 digits (123-346) should be considered invalid.
Try this one:
/^\d{1,2}(-\d{1,2})?$/
One or two digits, followed by, optionally, ( a hyphen followed by one or two digits)
Fairly easy:
^\d+(-\d+)?$
At least one (+) digit (\d), followed by an optional group containing a hyphen-minus (-), followed by at least one digit again.
For strings containing only that pattern the following should work
^(\d{2}-)?\d{2}$
A group of 2 digits followed by minus ending with a group of 2 digits without minus.
I am using the following regex to match an account number. When we originally put this regex together, the rule was that an account number would only ever begin with a single letter. That has since changed and I have an account number that has 3 letters at the beginning of the string.
I'd like to have a regex that will match a minimum of 1 letter and a maximum of 3 letters at the beginning of the string. The last issue is the length of the string. It can be as long as 9 characters and a minimum of 3.
Here is what I am currently using.
'/^([A-Za-z]{1})([0-9]{7})$/'
Is there a way to match all of this?
You want:
^[A-Za-z]([A-Za-z]{2}|[A-Za-z][0-9]|[0-9]{2})[0-9]{0,6}$
The initial [A-Za-z] ensures that it starts with a letter, the second bit ([A-Za-z]{2}|[A-Za-z][0-9]|[0-9]{2}) ensures that it's at least three characters long and consists of between one and three letters at the start, and the final bit [0-9]{0,6} allows you to go up to 9 characters in total.
Further explaining:
^ Start of string/line anchor.
[A-Za-z] First character must be alpha.
( [A-Za-z]{2} Second/third character are either alpha/alpha,
|[A-Za-z][0-9] alpha/digit,
|[0-9]{2} or digit/digit
) (also guarantees minimum length of three).
[0-9]{0,6} Then up to six digits (to give length of 3 thru 9).
$ End of string/line marker.
Try this:
'/^([A-Za-z]{1,3})([0-9]{0,6})$/'
That will give you from 1 to 3 letters and from 3 to 9 total characters.