Reputation: 45
I need to validate a user's password against the following requirements:
8 or more characters.
Contains 1 character and 1 number
Can enter letters, numbers, and symbols
Does anyone know how I can accomplish this using a RegEx?
I've made attempts to solve this problem on my own, but nothing I've tried so far as worked. The code for my latest attempt is below.
func isPasswordHasEightCharacter(password: String) -> Bool {
let passWordRegEx = "^.{8,}$"
let passwordTest = NSPredicate(format: "SELF MATCHES %@", passWordRegEx)
return passwordTest.evaluate(with: password)
}
func isPasswordHasNumberAndCharacter(password: String) -> Bool {
let passRegEx = "^(?=.*[a-z])(?=.*[0-9])"
let passwordTest = NSPredicate(format: "SELF MATCHES %@", passRegEx)
return passwordTest.evaluate(with: password)
}
func isPasswordHasNumberAndCharacterSign(password: String) -> Bool {
let passWordRegEx = "^(?!.[^a-zA-Z0-9@#${'$'}^+=])"
let passwordTest = NSPredicate(format: "SELF MATCHES %@", passWordRegEx)
return passwordTest.evaluate(with: password)
}
Upvotes: 3
Views: 6901
Reputation: 51861
In this solution each requirement is checked individually to avoid complex regular expressions. This solution supports variants of characters like ôöệż etc
func validatePassword(_ password: String) -> Bool {
//At least 8 characters
if password.count < 8 {
return false
}
//At least one digit
if password.range(of: #"\d+"#, options: .regularExpression) == nil {
return false
}
//At least one letter
if password.range(of: #"\p{Alphabetic}+"#, options: .regularExpression) == nil {
return false
}
//No whitespace charcters
if password.range(of: #"\s+"#, options: .regularExpression) != nil {
return false
}
return true
}
Some test cases
print(validatePassword("abc")) // --> false
print(validatePassword("abcdefgh")) // --> false
print(validatePassword("abcde fgh1")) // --> false
print(validatePassword("abcdefgh1")) // --> true
print(validatePassword("abcåäö123")) // --> true
print(validatePassword("ABC123€%&")) // --> true
print(validatePassword("@èệżôøö123")) // --> true
Upvotes: 1
Reputation: 626689
The main issue is that NSPredicate
with MATCHES
requires the full string to match and consume the whole input. Lookarounds - you are using lookaheads - do not consume text, that is, the texts they match are not added to the match value and the regex index stays where it was before attempting to match a lookaround pattern.
The last two parts can be thus fixed this way:
func isPasswordHasNumberAndCharacter(password: String) -> Bool {
let passRegEx = "(?=[^a-z]*[a-z])[^0-9]*[0-9].*"
let passwordTest = NSPredicate(format: "SELF MATCHES %@", passRegEx)
return passwordTest.evaluate(with: password)
}
func isPasswordHasNumberAndCharacterSign(password: String) -> Bool {
let passWordRegEx = "[a-zA-Z0-9!@#$%^&*]+"
let passwordTest = NSPredicate(format: "SELF MATCHES %@", passWordRegEx)
return passwordTest.evaluate(with: password)
}
The first part is OK, though you do not need the ^
and $
anchors (as the whole string input must match the pattern). However, to check a string length you do not even need a regex: see Get the length of a String.
Note:
^(?=[^a-z]*[a-z])[^0-9]*[0-9].*\z
matches a string that contains at least one lowercase ASCII letter and at least one ASCII digit^[a-zA-Z0-9!@#$%^&*]+$
will match a string that only contains ASCII letters, digits and some specific symbols. If you inlude a -
make sure it is at the end. Escape both [
and ]
if you need to add them, too.If you want to combine all that into 1 regex you could use
let passRegEx = "(?=[^a-z]*[a-z])(?=[^0-9]*[0-9])[a-zA-Z0-9!@#$%^&*]{8,}"
Or, if you are not using the regex with the NSPredicate
MATCHES
, with anchors:
let passRegEx = "\\A(?=[^a-z]*[a-z])(?=[^0-9]*[0-9])[a-zA-Z0-9!@#$%^&*]{8,}\\z"
Upvotes: 2