34

I have to detect whether a string contains any special characters. How can I check it? Does Swift support regular expressions?

var characterSet:NSCharacterSet = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
if (searchTerm!.rangeOfCharacterFromSet(characterSet).location == NSNotFound){
    println("Could not handle special characters")
}

I tried the code above, but it matches only if I enter the first character as a special character.

pkamb
  • 26,648
  • 20
  • 124
  • 157
iPhone Guy
  • 1,135
  • 3
  • 21
  • 33
  • 1
    I don't think it really matters, but warning to others: the string in this question with all the letters & numbers has an extra 'K' (...JKLKM...). That extra K has propagated through the 4 answers below that also use the string. – Dan Jackson Apr 07 '17 at 08:31

10 Answers10

99

Your code check if no character in the string is from the given set. What you want is to check if any character is not in the given set:

if (searchTerm!.rangeOfCharacterFromSet(characterSet.invertedSet).location != NSNotFound){
    println("Could not handle special characters")
}

You can also achieve this using regular expressions:

let regex = NSRegularExpression(pattern: ".*[^A-Za-z0-9].*", options: nil, error: nil)!
if regex.firstMatchInString(searchTerm!, options: nil, range: NSMakeRange(0, searchTerm!.length)) != nil {
    println("could not handle special characters")

}

The pattern [^A-Za-z0-9] matches a character which is not from the ranges A-Z, a-z, or 0-9.

Update for Swift 2:

let searchTerm = "a+b"

let characterset = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
if searchTerm.rangeOfCharacterFromSet(characterset.invertedSet) != nil {
    print("string contains special characters")
}

Update for Swift 3:

let characterset = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
if searchTerm.rangeOfCharacter(from: characterset.inverted) != nil {
    print("string contains special characters")
}
Martin R
  • 488,667
  • 78
  • 1,132
  • 1,248
10

This answer may help the people who are using Swift 4.1

func hasSpecialCharacters() -> Bool {

    do {
        let regex = try NSRegularExpression(pattern: ".*[^A-Za-z0-9].*", options: .caseInsensitive)
        if let _ = regex.firstMatch(in: self, options: NSRegularExpression.MatchingOptions.reportCompletion, range: NSMakeRange(0, self.count)) {
            return true
        }

    } catch {
        debugPrint(error.localizedDescription)
        return false
    }

    return false
}

Taken reference from @Martin R's answer.

Mahendra
  • 7,012
  • 2
  • 25
  • 50
6

With Swift 5 you can just do

if let hasSpecialCharacters =  "your string".range(of: ".*[^A-Za-z0-9].*", options: .regularExpression) != nil {}
Ali
  • 2,077
  • 19
  • 21
5

Inverting your character set will work, because in your character set you have all the valid characters:

var characterSet:NSCharacterSet = NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
if (searchTerm!.rangeOfCharacterFromSet(characterSet.invertedSet).location == NSNotFound){
    println("No special characters")
}

Hope this helps.. :)

pkamb
  • 26,648
  • 20
  • 124
  • 157
Rashad
  • 10,519
  • 4
  • 40
  • 67
  • Your check is the wrong way around. *"Could not handle special characters"* is printed if the string contains *no* special characters. – Martin R Dec 30 '14 at 09:57
  • Now it is correct (and equivalent to the code in my answer, which for some reason was downvoted :) – Martin R Dec 30 '14 at 10:01
  • @MartinR> I don't know. but yes yours and my solution is same. You got 2 upvotes though. :P – Rashad Dec 30 '14 at 10:02
  • Thanks for pointing out.Is it possible to achieve this using Regular expression? – iPhone Guy Dec 30 '14 at 10:08
5

Password validation With following:- (Password at least eight characters long, one special character, one uppercase, one lower case letter and one digit)

var isValidateSecialPassword : Bool {

        if(self.count>=8 && self.count<=20){
        }else{
            return false
        }
        let nonUpperCase = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZ").inverted
        let letters = self.components(separatedBy: nonUpperCase)
        let strUpper: String = letters.joined()

        let smallLetterRegEx  = ".*[a-z]+.*"
        let samlltest = NSPredicate(format:"SELF MATCHES %@", smallLetterRegEx)
        let smallresult = samlltest.evaluate(with: self)

        let numberRegEx  = ".*[0-9]+.*"
        let numbertest = NSPredicate(format:"SELF MATCHES %@", numberRegEx)
        let numberresult = numbertest.evaluate(with: self)

        let regex = try! NSRegularExpression(pattern: ".*[^A-Za-z0-9].*", options: NSRegularExpression.Options())
        var isSpecial :Bool = false
        if regex.firstMatch(in: self, options: NSRegularExpression.MatchingOptions(), range:NSMakeRange(0, self.count)) != nil {
            print("could not handle special characters")
            isSpecial = true
        }else{
            isSpecial = false
        }
        return (strUpper.count >= 1) && smallresult && numberresult && isSpecial
    }
Ilesh P
  • 3,360
  • 1
  • 21
  • 43
3

@Martin R answer is great, I just wanted to update it (the second part) to Swift 2.1 version

let regex = try! NSRegularExpression(pattern: ".*[^A-Za-z0-9].*", options: NSRegularExpressionOptions())
if regex.firstMatchInString(searchTerm!, options: NSMatchingOptions(), range:NSMakeRange(0, searchTerm!.characters.count)) != nil {
    print("could not handle special characters")
}

I used try! as we can be sure it create a regex, it doesn't base on any dynamic kind of a data

Julian
  • 8,989
  • 4
  • 45
  • 63
2

Depending on the definition of special characters, you could use this:

let chars =  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

chars.canBeConvertedToEncoding(NSASCIIStringEncoding)
pkamb
  • 26,648
  • 20
  • 124
  • 157
MirekE
  • 11,021
  • 5
  • 32
  • 27
1

Two Solutions:

1)

extension String {
    var stripped: String {
        let okayChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ")
        return self.filter {okayChars.contains($0) }
    }
}

2)

class TrimDictionary {

    static func trimmedWord(wordString: String) -> String {
        var selectedString = wordString
    
        let strFirst = selectedString.first
        let strLast = selectedString.last
    
        let characterset = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
        if strFirst?.description.rangeOfCharacter(from: characterset.inverted) != nil {
            selectedString = String(selectedString.dropFirst())
        }
        if strLast?.description.rangeOfCharacter(from: characterset.inverted) != nil {
            selectedString = String(selectedString.dropLast())
        }
        return selectedString
    }
}
pkamb
  • 26,648
  • 20
  • 124
  • 157
kishor0011
  • 928
  • 1
  • 19
  • 45
1

For the purpose of filename sanitization, I prefer to detect the invalid characters, rather than provide an allowed character set. After all, many non-English speaking users need accented characters. The following function is inspired by this gist:

func checkForIllegalCharacters(string: String) -> Bool {
    let invalidCharacters = CharacterSet(charactersIn: "\\/:*?\"<>|")
    .union(.newlines)
    .union(.illegalCharacters)
    .union(.controlCharacters)

    if string.rangeOfCharacter(from: invalidCharacters) != nil {
        print ("Illegal characters detected in file name")
        // Raise an alert here
        return true
    } else {
    return false
}
Ron Regev
  • 311
  • 1
  • 8
0

Mahendra's answer can be stripped down a bit by using an inversion(^) within the regex clause. Additionally, you don't need A-Z and a-z when using the caseInsensitive option, as Swift covers that eventuality for you:

extension String {

  func containsSpecialCharacters(string: String) -> Bool {
        
        do {
            let regex = try NSRegularExpression(pattern: "[^a-z0-9 ]", options: .caseInsensitive)
            if let _ = regex.firstMatch(in: string, options: [], range: NSMakeRange(0, string.count)) {
                return true
            } else {
                return false
            }
        } catch {
            debugPrint(error.localizedDescription)
            return true
        }
    }
Strafe86
  • 1
  • 1