Подтвердить что ты не робот

Количество находок персонажа в NSString

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

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

4b9b3361

Ответ 1

replaceOccurrencesOfString:withString:options:range: вернет количество символов, замененных в NSMutableString.

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

NSCharacterSet *charactersToCount = [NSCharacterSet characterSetWithCharactersInString:@"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

В наше время первое, что приходит мне в голову за что-то подобное: NSCountingSet

NSString *string = @"AAATTC";

NSMutableArray *array = [NSMutableArray array];

[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

Ответ 6

Ваше решение не сработало для меня, я добавил условие в цикле, чтобы увеличить 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];
   }
}

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

Ответ 7

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

NSString rangeOfCharacterFromSet:

или

rangeOfCharacterFromSet:options:range::

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

Ответ 8

Пример со сканером разбился на 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];
}

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

Ответ 9

Вот рабочая версия 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 []
        }
    }
}

Ответ 10

Сравнение производительности для различных решений Objective-C.

Предположим, что все методы ниже являются расширениями NSString (внутри @implementation NSString (CountOfOccurrences)).

В качестве примера я использовал случайно сгенерированную строку длиной 100000000, используя все латинские символы (CharacterSet(charactersIn: "\u{0020}"..."\u{036F}") в Swift). И персонаж для подсчета был @"a".

Тесты, выполненные на Xcode 10.3 на Симуляторе в конфигурации выпуска.

Быстрые решения (точная посимвольная эквивалентность)

Существует два способа подсчета персонажа: использовать NSLiteralSearch или нет. Количество будет отличаться, и производительность будет существенно зависеть. Для самых быстрых результатов мы выполним точную посимвольную эквивалентность. Ниже четыре решения дают очень близкие результаты производительности.

1. Самое быстрое решение: адаптация ответа CynicismRising.

Использование replaceOccurrencesOfString:withString:options:range:. Это самое быстрое решение во всех сценариях: даже если вы замените NSLiteralSearch на kNilOptions, вы все равно будете быстрее, чем решение для сканера pierrot3887.

- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
    return [[NSMutableString stringWithString:self] replaceOccurrencesOfString:stringToFind
                                                                    withString:stringToFind
                                                                       options:NSLiteralSearch
                                                                         range:NSMakeRange(0, self.length)];
}

2. На втором месте - еще одна адаптация ответа CynicismRising.

Использование stringByReplacingOccurrencesOfString:withString:options:range:.

- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
    NSString *strippedString = [self stringByReplacingOccurrencesOfString:stringToFind
                                                               withString:@""
                                                                  options:NSLiteralSearch
                                                                    range:NSMakeRange(0, self.length)];
    return (self.length - strippedString.length) / stringToFind.length;
}

3. Третье по быстродействию решение Жака.

Использование CFStringGetCharacterFromInlineBuffer. Смотрите fooobar.com/info/166909/....

4. В-четвертых, самое быстрое - преобразование моего быстрого ответа в Objective-C.

Использование rangeOfString:options:range:.

- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
    //assert(stringToFind.length);
    NSUInteger count = 0;
    NSRange searchRange = NSMakeRange(0, self.length);
    NSRange foundRange;
    while ((void)(foundRange = [self rangeOfString:stringToFind options:NSLiteralSearch range:searchRange]), foundRange.length) {
        count += 1;
        NSUInteger loc = NSMaxRange(foundRange);
        searchRange = NSMakeRange(loc, self.length - loc);
    }
    return count;
}

Медленные решения

Приведенные ниже решения не используют NSLiteralSearch и не выполняют точную посимвольную эквивалентность. Первые два, возможно, в 10 раз медленнее, чем быстрые решения, а последнее, может быть, в 100 раз медленнее.

5. Медленное решение: адаптация ответа pierrot3887

Использование scanUpToString:intoString:. Жаль, что NSScanner не предлагает опцию для точной посимвольной эквивалентности.

- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
    NSScanner *scanner = [NSScanner scannerWithString:self];
    scanner.charactersToBeSkipped = nil;
    scanner.caseSensitive = YES;
    NSUInteger numberOfOccurrences = 0;
    while (!scanner.isAtEnd) {
        [scanner scanUpToString:stringToFind intoString:nil];
        if (!scanner.isAtEnd) {
            numberOfOccurrences++;
            [scanner scanString:stringToFind intoString:nil];
        }
    }
    return numberOfOccurrences;
}

6. Более медленный раствор: раствор gbaor

Использование componentsSeparatedByString:. Относительно аргумента doable в одной строке, обратите внимание, что самое быстрое решение, приведенное выше, также является однострочным.

- (NSUInteger)countOccurrencesOfString:(NSString *)stringToFind
{
    return [self componentsSeparatedByString:stringToFind].count - 1;
}

7. Самое медленное решение: адаптация ответа vikingosegundo

Использование enumerateSubstringsInRange:options:usingBlock:.

- (NSUInteger)countOccurrencesOfCharacter:(NSString *)characterToFind
{
    __block NSUInteger counter = 0;
    [self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
        if ([characterToFind isEqualToString:substring]) counter += 1;
    }];
    return counter;
}