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!
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++;
}
}
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.
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:
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;
}
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.
}
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++
}
}
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");
}
Si vous voulez compter les mots , pas seulement les sous-chaînes, utilisez alors CFStringTokenizer .
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;
}
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"];
for(int i =0;i<htmlsource1.length-search.length;i++){
range = NSMakeRange(i,search.length);
checker = [htmlsource1 substringWithRange:range];
if ([search isEqualToString:checker]) {
count++;
}
}