web-dev-qa-db-fra.com

Nombre d'occurrences d'une sous-chaîne dans une chaîne NSS?

Comment puis-je obtenir le nombre de fois où une chaîne NSString (par exemple, @"cake") apparaît dans une chaîne NSString plus grande (par exemple, @"Cheesecake, Apple cake, and cherry pie")?

Je dois le faire sur beaucoup de chaînes, donc quelle que soit la méthode que j'utilise, elle devrait être relativement rapide.

Merci!

56
igul222

Ce n'est pas testé, mais devrait être un bon début.

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++; 
  }
}
98
Matthew Flaschen

Un regex comme celui ci-dessous devrait faire le travail sans interaction de boucle ...

Modifié

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);

Uniquement disponible sur iOS 4.x et supérieurs.

70
gwdp

cherchait une meilleure méthode que la mienne, mais voici un autre exemple:

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

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

J'aimerais savoir lequel est le plus efficace.

Et j'ai créé une catégorie NSString pour une meilleure utilisation:

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

appelez-le simplement par:

[text countOccurencesOfString:find];

Facultatif: vous pouvez le modifier pour qu'il ne respecte pas la casse en définissant options:

43
user207616

Il y a plusieurs façons de procéder. Vous pouvez appeler de manière itérative rangeOfString:options:range:, ou vous pourriez faire quelque chose comme:

NSArray * portions = [aString componentsSeparatedByString:@"cake"];
NSUInteger cakeCount = [portions count] - 1;

EDIT Je repensais à cette question et j'ai écrit un algorithme à temps linéaire pour faire la recherche (linéaire à la longueur de la chaîne de meule de foin):

+ (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;
}
23
Dave DeLong

Une solution plus rapide à taper, mais probablement moins efficace.

- (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.
}
11
Dash

Une solution rapide serait:

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
riik

Voici une version réalisée comme une extension de NSString (même idée que la réponse de Matthew 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");
}
3
Chris Johnsen

Si vous voulez compter les mots , pas seulement les sous-chaînes, utilisez alors CFStringTokenizer .

3
Peter Hosey

Voici une autre version en tant que catégorie sur 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
paulmelnikow

La réponse de Matthew Flaschen a été un bon début pour moi. Voici ce que j'ai fini par utiliser sous forme de méthode. J'ai adopté une approche légèrement différente de la boucle. Cela a été testé avec des chaînes vides passées à stringToCount et text et avec la stringToCount se produisant comme premier et/ou dernier caractère dans le texte.

J'utilise cette méthode régulièrement pour compter les paragraphes dans le texte passé (c'est-à-dire stringToCount = @ "\ r").

J'espère que cela sera utile à quelqu'un.

    - (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;
   }

Exemple d'appel en supposant que la méthode se trouve dans une classe nommée myHelperClass ...

int foundCount = [myHelperClass countString:@"n" inText:@"Now is the time for all good men to come to the aid of their country"];
1
user278859
for(int i =0;i<htmlsource1.length-search.length;i++){
  range = NSMakeRange(i,search.length);
  checker = [htmlsource1 substringWithRange:range];

  if ([search isEqualToString:checker]) {
   count++;

  }

 }
0
muhomania