[英]Fastest way to get array of NSRange objects for all uppercase letters in an NSString?
我需要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具有此功能。 閱讀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就是一個例子 - 它可能適合您的輸入,也可能不適合您的輸入。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.