问题
The following regex ensures a password contains at least one lowercase, one uppercase, one number, and one special character:
^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[^a-zA-Z0-9\s]).*$
That works. Building on this, I'd like to require that only two of these groups be fulfilled in order for a password to be valid.
For example, these would be valid passwords: aaaaa5, BFEWREWRE77, #2ccc.
Is there I way I can modify this regex to support this requirement?
回答1:
You can do it like this:
with spaces forbidden:
^(?=([A-Z]+|[a-z]+|[0-9]+|[^a-zA-Z0-9\s]+))\1\S+$
with spaces allowed:
^\s*(?=((?:[A-Z]+\s*)+|(?:[a-z]+\s*)+|(?:[0-9]+\s*)+|(?:[^a-zA-Z0-9\s]+\s*)+))\1.+$
explanations:
(?=([A-Z]+|[a-z]+|[0-9]+|[^a-zA-Z0-9\s]+))\1
emulates the atomic group (?>[A-Z]+|[a-z]+|[0-9]+|[^a-zA-Z0-9\s]+)
. So once a branch of this group is matched, the regex engine is no more allowed to backtrack inside the matched characters.
Since the quantifiers are greedy by default, all the characters of one of the categories are matched by the atomic group.
The next character matched by \S
or .
is obviously from a different character class than the one used in the group.
Note: for the second pattern, since there is no limitation about the characters used in the string, you don't need to test the string until the end, so you can write:
^\s*(?=((?:[A-Z]+\s*)+|(?:[a-z]+\s*)+|(?:[0-9]+\s*)+|(?:[^a-zA-Z0-9\s]+\s*)+))\1.
回答2:
Since you asked for this long regex:
^(?:(?=.*[A-Z])(?=.*[a-z])|(?=.*[A-Z])(?=.*[0-9])|(?=.*[A-Z])(?=.*[^a-zA-Z0-9\s])|(?=.*[a-z])(?=.*[0-9])|(?=.*[a-z])(?=.*[^a-zA-Z0-9\s])|(?=.*[0-9])(?=.*[^a-zA-Z0-9\s])).+$
RegEx Demo
回答3:
Most modern engines offer conditionals.
This is a simple example pattern that inhibits re-entering the same group
and guarantees X out of Y requirements.
In essence, the groups are matched in no particular order based on only
the quantifier number X, which is the criteria that causes an alignment.
Its very scalable, just add a new group in the alternation.
Set X for how many unique groups you want to match.
This one is set for 3 out of the 4.
And just finish it off with any boundary requirements you have, like^
+regex_below+.*$
.
Also, this consumes as it goes. It could easily be within a lookahead should there
be any length or other specific attributes.
Note that if the engine doesn't support conditionals (not all do), then this won't work.
# (?:.*?(?>((?(1)(?!))[a-z]+)|((?(2)(?!))[A-Z]+)|((?(3)(?!))[0-9]+)|((?(4)(?!))[^a-zA-Z0-9\s]+))){3}
(?:
.*?
(?>
( # (1)
(?(1) (?!) )
[a-z]+
)
| ( # (2)
(?(2) (?!) )
[A-Z]+
)
| ( # (3)
(?(3) (?!) )
[0-9]+
)
| ( # (4)
(?(4) (?!) )
[^a-zA-Z0-9\s]+
)
)
){3}
Sample output when X is 4 (out of 4)
** Grp 0 - ( pos 0 , len 11 )
B,B_+&*%#a0
** Grp 1 - ( pos 9 , len 1 )
a
** Grp 2 - ( pos 0 , len 1 )
B
** Grp 3 - ( pos 10 , len 1 )
0
** Grp 4 - ( pos 1 , len 1 )
,
回答4:
you would have to do something like :
^(
(?=1)(?=2)
|
(?=1)(?=3)
|
(?=1)(?=4)
|
(?=2)(?=3)
|
(?=2)(?=4)
|
(?=3)(?=4)
).*$
where 1,2,3 & 4 are your different patterns
来源:https://stackoverflow.com/questions/27110036/how-can-i-require-that-at-least-two-lookahead-patterns-match-within-one-regex