Количество вхождений подстроки в NSString? - PullRequest
55 голосов
/ 30 января 2010

Как узнать, сколько раз строка NSString (например, @"cake") появляется в большей строке NSString (например, @"Cheesecake, apple cake, and cherry pie")?

Мне нужно сделать это для большого количества строк, поэтому любой метод, который я использую, должен быть относительно быстрым.

Спасибо!

Ответы [ 13 ]

98 голосов
/ 30 января 2010

Это не проверено, но должно быть хорошим началом.

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++; 
  }
}
70 голосов
/ 19 июня 2012

Регулярное выражение, подобное приведенному ниже, должно выполнять работу без циклического взаимодействия ...

Отредактировано

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.x и старше.

43 голосов
/ 15 марта 2011

искал лучший метод, чем мой, но вот еще один пример:

NSString *find = @"cake";
NSString *text = @"Cheesecake, apple cake, and cherry pie";

NSInteger strCount = [text length] - [[text stringByReplacingOccurrencesOfString:find withString:@""] length];
strCount /= [find length];

Я хотел бы знать, какой из них более эффективен.

И я сделал категорию NSStringдля лучшего использования:

// 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:

23 голосов
/ 30 января 2010

Есть несколько способов сделать это.Вы могли бы итеративно вызвать 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;
}
11 голосов
/ 19 июля 2013

Более быстрое, но, вероятно, менее эффективное решение.

- (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.
}
3 голосов
/ 17 сентября 2015

Быстрое решение будет:

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++
     }
}
3 голосов
/ 11 апреля 2013

Вот еще одна версия в качестве категории на 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;
}
3 голосов
/ 03 марта 2010

Если вы хотите считать слов , а не только подстрок, используйте CFStringTokenizer .

3 голосов
/ 30 января 2010

Вот версия, сделанная как расширение NSString (та же идея, что и в ответе Мэтью Флэшена):

@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");
}
1 голос
/ 08 января 2011

Ответ Мэтью Флэшена стал для меня хорошим началом. Вот то, что я в конечном итоге использовал в виде метода. Я взял немного другой подход к петле. Это было проверено с пустыми строками, переданными в 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"];
...