2009-06-02 2 views
31

У меня есть NSString или NSMutableString и хотел бы получить количество вхождений определенного символа.Число находок символов в NSString

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

ответ

1

я бы, вероятно, использовать

NSString rangeOfCharacterFromSet:

или

rangeOfCharacterFromSet:options:range::

где набор является набор символов, которые вы ищете. Он возвращается с расположением первого символа, соответствующего набору. Сохраните массив или словарь и увеличьте количество символов, затем повторите.

+0

Если я правильно понимаю документацию, это даст диапазон любых символов в наборе. Но мне нужен счет * каждого * персонажа. – Elliot

+1

моя идея состоит в том, чтобы сохранить словарь символов char -> count, а затем получить char в указанном индексе и увеличить его количество в словаре ... или вы можете просто перебирать строку и проверять, находится ли каждый символ в вашем наборе, если это затем увеличивает его количество – stefanB

7

Всякий раз, когда вы ищете вещи в NSString, попробуйте использовать NSScanner первым.

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]; 
+0

Мне не удалось заставить это работать вообще. Я пытаюсь подсчитать количество пробелов. – lawrence

+3

@lawrence По умолчанию пробелы и пробелы игнорируются NSScanner. – borrrden

+3

Вы можете вызвать 'setCharactersToBeSkipped: (NSCharacterSet *) skipSet' с' nil', поскольку 'skipSet' - и' NSScanner' не пропустит никаких символов. –

1

Пример использования сканера на iPhone. Я нашел это решение:

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

Это работало для меня без сбоев. Надеюсь, это поможет!

2

Ваше решение не работает для меня, я добавил условие в цикле приращения numberOfChar только если mainScanner достиг конца строки:

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

Заметим, что это быстро исправить, я не у вас есть время, чтобы сделать изящное решение ...

96

Вы можете сделать это в одной строке. Например, это подсчитывает количество пробелов:

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

Создает временный массив, хотя .. Для скорости рекомендуется [ответ Жака] (http://stackoverflow.com/a/15947190/482601). – fabian789

+1

Считает ли это пробел в начале и конце 'yourString'? –

+0

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

22

Попробуйте эту категорию на 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 

Это один находится примерно в 5 раз быстрее, чем componentsSeparatedByString: подход.

+0

Эта категория работает нормально, но у меня есть вопрос: можете ли вы использовать UniChar (из CFString.h) и unichar (из NSString) как то же самое? – Bjinse

5

В настоящее время первое, что приходит мне на ум что-то вроде этого: NSCountedSet

NSString *string [email protected]"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); 
} 

журналы:

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

Вот Swift 3 рабочая версия, для NSRange, Range, String и NSString! Наслаждайтесь :)

/// 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 [] 
     } 
    } 
} 
Смежные вопросы