The easiest way is to use -rangeOfCharacterFromSet:options:range:
with [NSCharacterSet uppercaseLetterCharacterSet]
. By changing the search range for each call, you can easily find all uppercase letters. Something like the following will work to give you an NSArray of all ranges (encoded as NSValues):
- (NSArray *)rangesOfUppercaseLettersInString:(NSString *)str { NSCharacterSet *cs = [NSCharacterSet uppercaseLetterCharacterSet]; NSMutableArray *results = [NSMutableArray array]; NSRange searchRange = NSMakeRange(0, [str length]); NSRange range; while ((range = [str rangeOfCharacterFromSet:cs options:0 range:searchRange]).location != NSNotFound) { [results addObject:[NSValue valueWithRange:range]]; searchRange = NSMakeRange(NSMaxRange(range), [str length] - NSMaxRange(range)); } return results; }
Please note: this will not merge adjacent ranges into a single range, but it is easy enough to add.
Here's an alternative solution based on NSScanner:
- (NSArray *)rangesOfUppercaseLettersInString:(NSString *)str { NSCharacterSet *cs = [NSCharacterSet uppercaseLetterCharacterSet]; NSMutableArray *results = [NSMutableArray array]; NSScanner *scanner = [NSScanner scannerWithString:str]; while (![scanner isAtEnd]) { [scanner scanUpToCharactersFromSet:cs intoString:NULL];
Unlike the latter, it combines adjacent uppercase characters into a single range.
Edit: if you are looking for absolute speed, this one is likely to be the fastest of the 3 presented here, while maintaining proper Unicode support (note, I have not tried to compile this):
// returns a pointer to an array of NSRanges, and fills in count with the number of ranges // the buffer is autoreleased - (NSRange *)rangesOfUppercaseLettersInString:(NSString *)string count:(NSUInteger *)count { NSMutableData *data = [NSMutableData data]; NSUInteger numRanges = 0; NSUInteger length = [string length]; unichar *buffer = malloc(sizeof(unichar) * length); [string getCharacters:buffer range:NSMakeRange(0, length)]; NSCharacterSet *cs = [NSCharacterSet uppercaseLetterCharacterSet]; NSRange range = {NSNotFound, 0}; for (NSUInteger i = 0; i < length; i++) { if ([cs characterIsMember:buffer[i]]) { if (range.location == NSNotFound) { range = (NSRange){i, 0}; } range.length++; } else if (range.location != NSNotFound) { [data appendBytes:&range length:sizeof(range)]; numRanges++; range = (NSRange){NSNotFound, 0}; } } if (range.location != NSNotFound) { [data appendBytes:&range length:sizeof(range)]; numRanges++; } if (count) *count = numRanges; return [data bytes]; }
source share