Number of Occurrences of a Character in NSString

2019-01-07 17:59发布

问题:

I have an NSString or NSMutableString and would like to get the number of occurrences of a particular character.

I need to do this for quite a few characters -- uppercase English characters in this case -- so it would be nice for it to be quick.

回答1:

replaceOccurrencesOfString:withString:options:range: will return the number of characters replaced in a NSMutableString.

[string replaceOccurrencesOfString:@"A" 
                        withString:@"B" 
                           options:NSLiteralSearch 
                             range:NSMakeRange(0, [receiver length])];


回答2:

You can do this in one line. For example, this counts the number of spaces:

NSUInteger numberOfOccurrences = [[yourString componentsSeparatedByString:@" "] count] - 1;


回答3:

Try this category on NSString:

@implementation NSString (OccurrenceCount)

- (NSUInteger)occurrenceCountOfCharacter:(UniChar)character
{
    CFStringRef selfAsCFStr = (__bridge CFStringRef)self;

    CFStringInlineBuffer inlineBuffer;
    CFIndex length = CFStringGetLength(selfAsCFStr);
    CFStringInitInlineBuffer(selfAsCFStr, &inlineBuffer, CFRangeMake(0, length));

    NSUInteger counter = 0;

    for (CFIndex i = 0; i < length; i++) {
        UniChar c = CFStringGetCharacterFromInlineBuffer(&inlineBuffer, i);
        if (c == character) counter += 1;
    }

    return counter;
}

@end

This one is approximately 5 times faster than the componentsSeparatedByString: approach.



回答4:

Whenever you are looking for things in a NSString, try using NSScanner first.

NSString *yourString = @"ABCCDEDRFFED"; // For example
NSScanner *scanner = [NSScanner scannerWithString:yourString];

NSCharacterSet *charactersToCount = @"C" // For example
NSString *charactersFromString;

if (!([scanner scanCharactersFromSet:charactersToCount 
                          intoString:&charactersFromString])) {
    // No characters found
    NSLog(@"No characters found");
}

// should return 2 for this
NSInteger characterCount = [charactersFromString length];


回答5:

Nowadays the first thing that come to my mind for something like that: NSCountedSet

NSString *string =@"AAATTC";

NSMutableArray *array = [@[] mutableCopy];

[string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
    [array addObject:substring];
}] ;
NSCountedSet * set = [[NSCountedSet alloc] initWithArray:array];

for (NSString *nucleobase in @[@"C", @"G", @"A", @"T"]){
    NSUInteger count =  [set countForObject:nucleobase];
    NSLog(@"%@: %lu", nucleobase, (unsigned long)count);
}

logs:

C: 1
G: 0
A: 3
T: 2


回答6:

Your solution did not work for me, I added a condition in the loop to increment numberOfChar only if mainScanner has reached the end of the string :

NSString *yourString = @"ABCCDEDRFFED"; // For example
NSScanner *mainScanner = [NSScanner scannerWithString:yourString];
NSString *temp;
NSInteger numberOfChar=0;
while(![mainScanner isAtEnd])
{
   [mainScanner scanUpToString:@"C" intoString:&temp];
   if(![mainScanner isAtEnd]) {
      numberOfChar++;
      [mainScanner scanString:@"C" intoString:nil];
   }
}

Note that this is a quick fix, I don't have time to make an elegant solution...



回答7:

I would probably use

NSString rangeOfCharacterFromSet:

or

rangeOfCharacterFromSet:options:range::

where the set is the set of characters you're searching for. It returns with the location of first character matching the set. Keep array or dictionary and increment the count for character, then repeat.



回答8:

The example with the Scanner was crashing on iPhone. I found this solution :

NSString *yourString = @"ABCCDEDRFFED"; // For example
NSScanner *mainScanner = [NSScanner scannerWithString:yourString];
NSString *temp;
NSInteger numberOfChar=0;
while(![mainScanner isAtEnd])
{
   [mainScanner scanUpToString:@"C" intoString:&temp];
   numberOfChar++;
   [mainScanner scanString:@"C" intoString:nil];
}

It worked for me without crash. Hope it can help !



回答9:

Here is a Swift 3 working version, for NSRange, Range, String and NSString! Enjoy :)

/// All ranges using NSString and NSRange
/// Is usually used together with NSAttributedString

extension NSString {
    public func ranges(of searchString: String, options: CompareOptions = .literal, searchRange: NSRange? = nil) -> [NSRange] {
        let searchRange = searchRange ?? NSRange(location: 0, length: self.length)
        let subRange = range(of: searchString, options: options, range: searchRange)
        if subRange.location != NSNotFound {

            let nextRangeStart = subRange.location + subRange.length
            let nextRange = NSRange(location: nextRangeStart, length: searchRange.location + searchRange.length - nextRangeStart)
            return [subRange] + ranges(of: searchString, options: options, searchRange: nextRange)
        } else {
            return []
        }
    }
}

/// All ranges using String and Range<Index>
/// Is usually used together with NSAttributedString

extension String {
    public func ranges(of searchString: String, options: CompareOptions = [], searchRange: Range<Index>? = nil ) -> [Range<Index>] {
        if let range = range(of: searchString, options: options, range: searchRange, locale: nil) {

            let nextRange = range.upperBound..<(searchRange?.upperBound ?? endIndex)
            return [range] + ranges(of: searchString, searchRange: nextRange)
        } else {
            return []
        }
    }
}