获取NSString中所有大写字母的NSRange对象数组的最快方法?

我需要NSRange对象作为给定NSString中每个大写字母的位置,以输入自定义属性字符串类的方法。

当然有很多方法可以实现这一点,例如rangeOfString:options:使用NSRegularExpressionSearch或使用RegexKitLite在遍历字符串时单独获取每个匹配。

完成此任务的最快绩效方法是什么?

最简单的方法可能是使用-rangeOfCharacterFromSet:options:range: with [NSCharacterSet uppercaseLetterCharacterSet] 。 通过修改每次调用搜索的范围,您可以非常轻松地找到所有大写字母。 类似下面的内容将为您提供所有范围的NSArray(编码为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; } 

请注意,这不会将相邻范围合并为单个范围,但这很容易添加。

这是基于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]; // skip non-uppercase characters NSString *temp; NSUInteger location = [scanner scanLocation]; if ([scanner scanCharactersFromSet:cs intoString:&temp]) { // found one (or more) uppercase characters NSRange range = NSMakeRange(location, [temp length]); [results addObject:[NSValue valueWithRange:range]]; } } return results; } 

与上一个不同,这个将相邻的大写字符合并为一个范围。

编辑 :如果你正在寻找绝对速度,这个可能是这里提出的3中最快的,同时仍然保持正确的unicode支持(注意,我还没有尝试编译这个):

 // 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]; } 

使用RegexKitLite 4.0+和支持Blocks的运行时,这可能非常有趣:

 NSString *string = @"A simple String to TEST for Upper Case Letters."; NSString *regex = @"\\p{Lu}"; [string enumerateStringsMatchedByRegex:regex options:RKLNoOptions inRange:NSMakeRange(0UL, [string length]) error:NULL enumerationOptions:RKLRegexEnumerationCapturedStringsNotRequired usingBlock:^(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) { NSLog(@"Range: %@", NSStringFromRange(capturedRanges[0])); }]; 

正则表达式\p{Lu}说“匹配所有字符与’Letter’的Unicode属性也是’大写”。

选项RKLRegexEnumerationCapturedStringsNotRequired告诉RegexKitLite它不应该创建NSString对象并通过capturedStrings[]传递它们。 这节省了相当多的时间和内存。 唯一传递给块的是通过capturedRanges[]进行匹配的NSRange值。

这有两个主要部分,第一个是RegexKitLite方法:

 [string enumerateStringsMatchedByRegex:regex options:RKLNoOptions inRange:NSMakeRange(0UL, [string length]) error:NULL enumerationOptions:RKLRegexEnumerationCapturedStringsNotRequired usingBlock:/* ... */ ]; 

…第二个是作为该方法的参数传递的块:

 ^(NSInteger captureCount, NSString * const capturedStrings[captureCount], const NSRange capturedRanges[captureCount], volatile BOOL * const stop) { /* ... */ } 

它在某种程度上取决于字符串的大小,但是我能想到的绝对最快的方式(注意:国际化安全性无法保证,甚至没有预期!大写的概念是否适用于日语?)是:

1)获取指向字符串的原始C字符串的指针,如果它足够小,最好在堆栈缓冲区中。 CFString具有此function。 阅读CFString.h中的注释。

2)malloc()一个足够大的缓冲区,可以在字符串中为每个字符保存一个NSRange。

3)像这样的东西(完全未经测试,写入此文本字段,原谅错误和拼写错误)

 NSRange *bufferCursor = rangeBuffer; NSRange range = {NSNotFound, 0}; for (int idx = 0; idx < numBytes; ++idx) { if (isupper(buffer[idx])) { if (range.length > 0) { //extend a range, we found more than one uppercase letter in a row range.length++; } else { //begin a range range.location = idx; range.length = 1; } } else if (range.location != NSNotFound) { //end a range, we hit a lowercase letter *bufferCursor = range; bufferCursor++; range.location = NSNotFound; } } 

4)realloc()将范围缓冲区调回到你实际使用的大小(可能需要保持一个范围的计数开始这样做)

isupper *这样的函数与-[NSString characterAtIndex:]相结合将会非常快。

* isupper是一个例子 – 它可能适合您的输入,也可能不适合您的输入。