Количество вхождений подстроки в NSString?
Как я могу получить количество раз NSString (например, @"cake"
) появляется в большей NSString (например,@"Cheesecake, apple cake, and cherry pie"
)?
мне нужно сделать это на большом количестве строк, поэтому любой метод, который я использую, должен быть относительно быстрым.
спасибо!
13 ответов:
Это не проверено, но должно быть хорошим началом.
NSUInteger count = 0, length = [str length]; NSRange range = NSMakeRange(0, length); while(range.location != NSNotFound) { range = [str rangeOfString: @"cake" options:0 range:range]; if(range.location != NSNotFound) { range = NSMakeRange(range.location + range.length, length - (range.location + range.length)); count++; } }
регулярное выражение, подобное приведенному ниже, должно выполнять работу без взаимодействия с циклом...
редактировать
NSString *string = @"Lots of cakes, with a piece of cake."; NSError *error = NULL; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"cake" options:NSRegularExpressionCaseInsensitive error:&error]; NSUInteger numberOfMatches = [regex numberOfMatchesInString:string options:0 range:NSMakeRange(0, [string length])]; NSLog(@"Found %i",numberOfMatches);
доступно только на iOS 4.Икс и начальство.
искал лучший способ, чем мой, но вот еще один пример:
NSString *find = @"cake"; NSString *text = @"Cheesecake, apple cake, and cherry pie"; NSInteger strCount = [text length] - [[text stringByReplacingOccurrencesOfString:find withString:@""] length]; strCount /= [find length];
Я хотел бы знать, какой из них более эффективен.
и я для лучшего использования:
// NSString+CountString.m @interface NSString (CountString) - (NSInteger)countOccurencesOfString:(NSString*)searchString; @end @implementation NSString (CountString) - (NSInteger)countOccurencesOfString:(NSString*)searchString { NSInteger strCount = [self length] - [[self stringByReplacingOccurrencesOfString:searchString withString:@""] length]; return strCount / [searchString length]; } @end
просто назовите его:
[text countOccurencesOfString:find];
необязательный: вы можете изменить его для поиска без учета регистра, определив
options:
есть несколько способов сделать это. Вы можете итеративно вызвать
rangeOfString:options:range:
, или вы могли бы сделать что-то вроде:NSArray * portions = [aString componentsSeparatedByString:@"cake"]; NSUInteger cakeCount = [portions count] - 1;
EDIT я снова думал об этом вопросе, и я написал алгоритм линейного времени для поиска (линейный по длине строки стога сена):
+ (NSUInteger) numberOfOccurrencesOfString:(NSString *)needle inString:(NSString *)haystack { const char * rawNeedle = [needle UTF8String]; NSUInteger needleLength = strlen(rawNeedle); const char * rawHaystack = [haystack UTF8String]; NSUInteger haystackLength = strlen(rawHaystack); NSUInteger needleCount = 0; NSUInteger needleIndex = 0; for (NSUInteger index = 0; index < haystackLength; ++index) { const char thisCharacter = rawHaystack[index]; if (thisCharacter != rawNeedle[needleIndex]) { needleIndex = 0; //they don't match; reset the needle index } //resetting the needle might be the beginning of another match if (thisCharacter == rawNeedle[needleIndex]) { needleIndex++; //char match if (needleIndex >= needleLength) { needleCount++; //we completed finding the needle needleIndex = 0; } } } return needleCount; }
более быстрый тип, но, вероятно, менее эффективное решение.
- (int)numberOfOccurencesOfSubstring:(NSString *)substring inString:(NSString*)string { NSArray *components = [string componentsSeparatedByString:substring]; return components.count-1; // Two substring will create 3 separated strings in the array. }
вот версия, сделанная как расширение к
NSString
(тот же принцип, что ответа от Матфея горнолыжный подъемник flaschen-х):@interface NSString (my_substr_search) - (unsigned) countOccurencesOf: (NSString *)subString; @end @implementation NSString (my_substring_search) - (unsigned) countOccurencesOf: (NSString *)subString { unsigned count = 0; unsigned myLength = [self length]; NSRange uncheckedRange = NSMakeRange(0, myLength); for(;;) { NSRange foundAtRange = [self rangeOfString:subString options:0 range:uncheckedRange]; if (foundAtRange.location == NSNotFound) return count; unsigned newLocation = NSMaxRange(foundAtRange); uncheckedRange = NSMakeRange(newLocation, myLength-newLocation); count++; } } @end <somewhere> { NSString *haystack = @"Cheesecake, apple cake, and cherry pie"; NSString *needle = @"cake"; unsigned count = [haystack countOccurencesOf: needle]; NSLog(@"found %u time%@", count, count == 1 ? @"" : @"s"); }
Если вы хотите считать слова, а не только подстроки, а затем использовать CFStringTokenizer.
вот еще одна версия как категория на NSString:
-(NSUInteger) countOccurrencesOfSubstring:(NSString *) substring { if ([self length] == 0 || [substring length] == 0) return 0; NSInteger result = -1; NSRange range = NSMakeRange(0, 0); do { ++result; range = NSMakeRange(range.location + range.length, self.length - (range.location + range.length)); range = [self rangeOfString:substring options:0 range:range]; } while (range.location != NSNotFound); return result; }
быстрое решение будет:
var numberOfSubstringAppearance = 0 let length = count(text) var range: Range? = Range(start: text.startIndex, end: advance(text.startIndex, length)) while range != nil { range = text.rangeOfString(substring, options: NSStringCompareOptions.allZeros, range: range, locale: nil) if let rangeUnwrapped = range { let remainingLength = length - distance(text.startIndex, rangeUnwrapped.endIndex) range = Range(start: rangeUnwrapped.endIndex, end: advance(rangeUnwrapped.endIndex, remainingLength)) numberOfSubstringAppearance++ } }
Мэтью горнолыжный подъемник flaschen-это был хороший старт для меня. Вот что я в конечном итоге использовал в виде метода. Я взял немного другой подход к циклу. Это было проверено с пустыми строками, переданными в stringToCount и text, и с stringToCount, происходящим как первый и / или последний символы в тексте.
Я использую этот метод регулярно для подсчета абзацев в переданном тексте (т. е. stringToCount = @ "\r").
надеюсь, что это пригодиться кто-то.
- (int)countString:(NSString *)stringToCount inText:(NSString *)text{ int foundCount=0; NSRange range = NSMakeRange(0, text.length); range = [text rangeOfString:stringToCount options:NSCaseInsensitiveSearch range:range locale:nil]; while (range.location != NSNotFound) { foundCount++; range = NSMakeRange(range.location+range.length, text.length-(range.location+range.length)); range = [text rangeOfString:stringToCount options:NSCaseInsensitiveSearch range:range locale:nil]; } return foundCount; }
пример вызова предполагая, что метод находится в классе с именем myHelperClass...
int foundCount = [myHelperClass countString:@"n" inText:@"Now is the time for all good men to come to the aid of their country"];
for(int i =0;i<htmlsource1.length-search.length;i++){ range = NSMakeRange(i,search.length); checker = [htmlsource1 substringWithRange:range]; if ([search isEqualToString:checker]) { count++; } }
нет встроенного метода. Я бы предложил вернуть c-строку и использовать общий алгоритм стиля c-строки для подсчета подстрок... если вам действительно нужно, чтобы это было быстро.
Если вы хотите остаться в Objective C, это ссылке может помочь. Он описывает основной поиск подстроки для NSString. Если вы работаете с диапазонами, настраиваете и подсчитываете, то у вас будет "чистое" решение Objective C... хотя и медленно.
-(IBAction)search:(id)sender{ int maincount = 0; for (int i=0; i<[self.txtfmainStr.text length]; i++) { char c =[self.substr.text characterAtIndex:0]; char cMain =[self.txtfmainStr.text characterAtIndex:i]; if (c == cMain) { int k=i; int count=0; for (int j = 0; j<[self.substr.text length]; j++) { if (k ==[self.txtfmainStr.text length]) { break; } if ([self.txtfmainStr.text characterAtIndex:k]==[self.substr.text characterAtIndex:j]) { count++; } if (count==[self.substr.text length]) { maincount++; } k++; } } NSLog(@"%d",maincount); } }