Количество находок персонажа в NSString
У меня есть NSString
или NSMutableString
и хотел бы получить количество вхождений определенного символа.
Мне нужно сделать это для целого ряда символов - в этом случае заглавные английские символы - так что было бы хорошо, если бы он был быстрым.
Ответы
Ответ 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;
}