カスタム属性文字列クラスのメソッドに入力するために、指定された NSString の各大文字の位置に NSRange オブジェクトが必要です。
もちろん、rangeOfString:options: で NSRegularExpressionSearch を使用したり、RegexKitLite を使用して文字列をたどりながら各一致を個別に取得したりするなど、これを実現する方法はかなりあります。
このタスクを達成するための最速の方法は何ですか?
カスタム属性文字列クラスのメソッドに入力するために、指定された NSString の各大文字の位置に NSRange オブジェクトが必要です。
もちろん、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;
}
これは、隣接する範囲を 1 つの範囲に結合するわけではありませんが、追加するのは簡単です。
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;
}
最後のものとは異なり、これは隣接する大文字を 1 つの範囲に結合します。
編集:絶対的な速度を探している場合、これはここに提示されている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+ を使用すると、非常に簡単に実行できます。
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}
は、「「大文字」でもある「文字」のUnicodeプロパティを持つすべての文字に一致する」と述べています。
このオプションRKLRegexEnumerationCapturedStringsNotRequired
は、RegexKitLite に、NSString
オブジェクトを作成して 経由で渡してはならないことを伝えますcapturedStrings[]
。これにより、かなりの時間とメモリが節約されます。ブロックに渡される唯一のものは、NSRange
を介した一致の値ですcapturedRanges[]
。
これには 2 つの主要部分があり、1 つ目は RegexKitLite メソッドです。
[string enumerateStringsMatchedByRegex:regex
options:RKLNoOptions
inRange:NSMakeRange(0UL, [string length])
error:NULL
enumerationOptions:RKLRegexEnumerationCapturedStringsNotRequired
usingBlock:/* ... */
];
... 2 番目は、そのメソッドに引数として渡されるブロックです。
^(NSInteger captureCount,
NSString * const capturedStrings[captureCount],
const NSRange capturedRanges[captureCount],
volatile BOOL * const stop) { /* ... */ }
文字列のサイズに多少依存しますが、私が考えることができる絶対的な最速の方法は次のとおりです (注: 国際化の安全性は保証されていないか、期待さえされていません! 大文字の概念は日本語にも適用されますか?)
1) 文字列の生の C 文字列へのポインタを取得します。十分に小さい場合は、できればスタック バッファ内にあります。CFString には、このための関数があります。CFString.h のコメントを読んでください。
2) 文字列内の文字ごとに 1 つの NSRange を保持するのに十分な大きさのバッファを malloc() します。
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は一例です-入力に適している場合とそうでない場合があります。