How to check if a string only contains alphanumeric characters in objective C?

回眸只為那壹抹淺笑 提交于 2019-11-28 04:05:19
Jason Harwig

If you don't want to bring in a regex library for this one task...

NSString *str = @"aA09";
NSCharacterSet *alphaSet = [NSCharacterSet alphanumericCharacterSet];
BOOL valid = [[str stringByTrimmingCharactersInSet:alphaSet] isEqualToString:@""]; 
NSResponder

This will work:

@implementation NSString (alphaOnly)

- (BOOL) isAlphaNumeric
{
    NSCharacterSet *unwantedCharacters = 
       [[NSCharacterSet alphanumericCharacterSet] invertedSet];

    return ([self rangeOfCharacterFromSet:unwantedCharacters].location == NSNotFound);
}

@end

You can use this regular expression library for ObjectiveC. Use the following regex to match:

^[a-zA-Z0-9]*$
JosephH

The NSCharacterSet based answers do not give the results you might expect for Japanese etc text, often claiming that they do contain alphanumeric characters - the test being performed boils down to 'are there only letters or numbers', and Japanese (etc) characters count as 'Letters'.

If you're trying to check Latin characters vs a foreign language (eg. Japanese), then the answer from " How to determine if an NSString is latin based? " may help:

BOOL isLatin = [myString canBeConvertedToEncoding:NSISOLatin1StringEncoding];

NSASCIIStringEncoding could also be used instead of NSISOLatin1StringEncoding to further restrict the valid characters. You could also test using NSCharacterSet afterwards, to exclude special characters like !, #, etc.

I've run some rather extensive performance testing and there are several considerations to take into account when choosing how to validate your alphanumeric strings. First, of course, is that you may not even care about performance. If your app rarely validates strings, or perhaps even only does it once, whatever method that gives you the behavior you want is fine. Beyond that, here are my performance results.

For custom character sets (say alphanumeric characters, without Unicode characters or marks), this is fastest for an initial run:

NSCharacterSet *alphanumericSet = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"];
NSString *result = [self stringByTrimmingCharactersInSet:alphanumericSet];

return [result isEqualToString:@""];

If you're okay using a precomputed character set like [NSCharacterSet alphanumericCharacterSet] then this was fastest:

NSCharacterSet *alphanumericSet = [NSCharacterSet alphanumericCharacterSet];
alphanumericSet = alphanumericSet.invertedSet;
NSRange range = [self rangeOfCharacterFromSet:alphanumericSet];

return (range.location == NSNotFound);

Caching the character set in a static variable using dispatch_once can help quite a bit if you run these validations repeatedly. In that case, if you're certain you can absorb the initial compilation time, using a regex actually turns out to be the fastest for custom character sets:

static NSRegularExpression *alphanumericRegex;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    alphanumericRegex = [NSRegularExpression regularExpressionWithPattern:@"^[a-zA-Z0-9]*$" options:NSRegularExpressionCaseInsensitive error:nil];
});
NSUInteger numberOfMatches = [alphanumericRegex numberOfMatchesInString:self options:0 range:NSMakeRange(0, self.length)];

return (numberOfMatches == 1);

If you don't wish to use the regex, the custom set version of the cached rangeOfCharacterFromSet edges out the cached stringByTrimmingCharactersInCharacterSet: method:

static NSCharacterSet *alphanumericSet;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    alphanumericSet = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"];
    alphanumericSet = alphanumericSet.invertedSet;
});

NSRange range = [self rangeOfCharacterFromSet:alphanumericSet];

return (range.location == NSNotFound);

For precomputed sets, the cached rangeOfCharacterFromSet: method was again the fastest:

static NSCharacterSet *alphanumericSet;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    alphanumericSet = [NSCharacterSet alphanumericCharacterSet];
    alphanumericSet = alphanumericSet.invertedSet;
});

NSRange range = [self rangeOfCharacterFromSet:alphanumericSet];

return (range.location == NSNotFound);

And for everyone's information, the isSupersetOfSet: method was the slowest, whether cached or not. Looks like isSupersetOfSet: is pretty slow.

NSCharacterSet *stringSet = [NSCharacterSet characterSetWithCharactersInString:self];
NSCharacterSet *alphanumericSet = [NSCharacterSet alphanumericCharacterSet];

return [alphanumericSet isSupersetOfSet:stringSet];

I didn't do any testing of the underlying CFCharacterSet functions.

- (BOOL)isAlphaNumeric
{
     NSCharacterSet *s = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'. "];
     s = [s invertedSet];
     NSRange r = [self rangeOfCharacterFromSet:s];
     if (r.location == NSNotFound) {
         return NO;
     } else {
       return YES;
    }
}

Has the flexibility to add/ subtract new characters like whitespaces

P.S This method can be copy/paste in NSString category

I really like the RegexKit Lite Framework. It uses the ICU regex library, which is already included with OSX and is unicode-safe.

NSString *str = @"testString";
[str isMatchedByRegex:@"^[a-zA-Z0-9]*$"]; // strict ASCII-match
[str isMatchedByRegex:@"^[\p{L}\p{N}]*$"]; // unicode letters and numbers match

You can use NSString regular expression capabilities, introduced in iOS 3.2:

- (BOOL)isAlphanumeric:(NSString *)string {
    return [string rangeOfString:@"^[a-zA-Z0-9]+$" options:NSRegularExpressionSearch].location != NSNotFound;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!