Мне нужно найти все ключевые слова в большом NSString
(для анализа исходного кода), и моя текущая реализация слишком медленная, но я не уверен, как ее улучшить.
Я использую NSRegularExpression
, исходя из предположения, что он более оптимизирован, чем все, что я мог бы написать, но производительность ниже, чем я ожидал. Кто-нибудь знает более быстрый способ реализовать это?
Строка назначения будет содержать символы utf-8, но сами ключевые слова всегда будут буквенно-цифровыми ascii. Я полагаю, что это можно было бы использовать, чтобы немного оптимизировать вещи?
@implementation MyClass
// i'm storing the regular expression in a static variable, since it never changes and I need to re-use it often
static NSRegularExpression *keywordsExpression;
+ (void)initialize
{
[super initialize];
NSArray *keywords = [NSArray arrayWithObjects:@"accumsan", @"adipiscing", @"aliquam", @"aliquet", @"amet", @"ante", @"arcu", @"at", @"commodo", @"congue", @"consectetur", @"consequat", @"convallis", @"cras", @"curabitur", @"cursus", @"dapibus", @"diam", @"dolor", @"dui", @"elit", @"enim", @"erat", @"eros", @"est", @"et", @"eu", @"felis", @"fermentum", @"gravida", @"iaculis", @"id", @"imperdiet", @"integer", @"ipsum", @"lacinia", @"lectus", @"leo", nil];
NSString *pattern = [NSString stringWithFormat:@"\\b(%@)\\b", [keywords componentsJoinedByString:@"|"]; // \b(accumsan|adipiscing|aliquam|…)\b
keywordsExpression = [NSRegularExpression regularExpressionWithPattern:pattern] options:NSRegularExpressionCaseInsensitive error:NULL];
}
// this method will be called in quick succession, I need it to be a able to run tens
// of thousands of times per second. The target string is big (50KB or so), but the
// search range is short, rarely more than 30 characters
- (NSRange)findNextKeyword:(NSString *)string inRange:(NSRange)range
{
return [keywordsExpression rangeOfFirstMatchInString:string options:0 range:range];
}
@end
РЕДАКТИРОВАТЬ Согласно ответу @ CodeBrickie, я обновил свой код, чтобы выполнить поиск по регулярному выражению один раз по всей строке и сохранить совпадения в кэшированном NSIndexSet
, затем каждый раз, когда вызывается метод, он ищет в NSIndexSet
диапазоны ключевых слов, а не ищет строку. Результат примерно на порядок быстрее:
@implementation MyClass
static NSRegularExpression *keywordsExpression;
static NSIndexSet *keywordIndexes = nil;
+ (void)initialize
{
[super initialize];
NSArray *keywords = [NSArray arrayWithObjects:@"accumsan", @"adipiscing", @"aliquam", @"aliquet", @"amet", @"ante", @"arcu", @"at", @"commodo", @"congue", @"consectetur", @"consequat", @"convallis", @"cras", @"curabitur", @"cursus", @"dapibus", @"diam", @"dolor", @"dui", @"elit", @"enim", @"erat", @"eros", @"est", @"et", @"eu", @"felis", @"fermentum", @"gravida", @"iaculis", @"id", @"imperdiet", @"integer", @"ipsum", @"lacinia", @"lectus", @"leo", nil];
NSString *pattern = [NSString stringWithFormat:@"\\b(%@)\\b", [keywords componentsJoinedByString:@"|"]; // \b(accumsan|adipiscing|aliquam|…)\b
keywordsExpression = [NSRegularExpression regularExpressionWithPattern:pattern] options:NSRegularExpressionCaseInsensitive error:NULL];
}
- (void)prepareToFindKeywordsInString:(NSString *)string
{
NSMutableIndexSet *keywordIndexesMutable = [[NSIndexSet indexSet] mutableCopy];
[keywordsExpression enumerateMatchesInString:string options:0 range:NSMakeRange(0, string.length) usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
[keywordIndexesMutable addIndexesInRange:match.range];
}];
keywordIndexes = [keywordIndexesMutable copy];
}
- (NSRange)findNextKeyword:(NSString *)string inRange:(NSRange)range
{
NSUInteger foundKeywordMax = (foundCharacterSetRange.location == NSNotFound) ? string.length : foundCharacterSetRange.location;
NSRange foundKeywordRange = NSMakeRange(NSNotFound, 0);
for (NSUInteger index = startingAt; index < foundKeywordMax; index++) {
if ([keywordIndexes containsIndex:index]) {
if (foundKeywordRange.location == NSNotFound) {
foundKeywordRange.location = index;
foundKeywordRange.length = 1;
} else {
foundKeywordRange.length++;
}
} else {
if (foundKeywordRange.location != NSNotFound) {
break;
}
}
}
return foundKeywordRange;
}
@end
Кажется, это работает хорошо, и производительность поднимается настолько, насколько я хочу. Я хотел бы подождать немного дольше, чтобы увидеть, есть ли еще предложения, прежде чем принять это.