3

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)
}
6
  • Have you debugged your functions? What exactly happened? Any error was thrown? Commented Sep 3, 2019 at 12:17
  • By letters, numbers and symbols, do you mean any non-whitespace chars? Commented Sep 3, 2019 at 12:17
  • check stackoverflow.com/questions/39284607/… Commented Sep 3, 2019 at 12:29
  • @WiktorStribiżew yes Commented Sep 3, 2019 at 12:33
  • Ok, see the answer below. Commented Sep 3, 2019 at 12:37

2 Answers 2

2

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"
Sign up to request clarification or add additional context in comments.

7 Comments

The last function I want only containt letters, numbers, and symbols like (!@#$%^&*). when I enter Special characters like " | ; , or katakana Japanese then return false
@HiệpChelsea So, do you mean to only match ASCII letters, numbers and some specific symbols then? I updated the answer. Please check.
yes, when I enter some characters I want show the message : "An invalid value has been entered "
@HiệpChelsea So, show it. The functions above should work now. Now you know why your regexps failed. The rest is up to you and does not pertain to the current question.
now I want white space in between text, top and last non-white space. I try : ^(?=\\S*)(?=\\s*)[a-zA-Z0-9!@#$%^&*]+(?=\\S*)$ but not work. can you help me?
|
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.