2009-06-02 14 views
31

Tengo NSString o NSMutableString y me gustaría obtener el número de apariciones de un personaje en particular.Número de apariciones de un personaje en NSString

Necesito hacer esto para bastantes caracteres, mayúsculas en inglés en este caso, así que sería bueno que fuera rápido.

Respuesta

1

utilizaría probablemente

NSString rangeOfCharacterFromSet:

o

rangeOfCharacterFromSet:options:range::

donde el conjunto es el conjunto de caracteres que está buscando. Vuelve con la ubicación del primer carácter que coincide con el conjunto. Mantenga la matriz o el diccionario e incremente el recuento de caracteres, luego repita.

+0

Si entiendo la Documentación correctamente, esto daría el rango de cualquiera de los caracteres en el conjunto. Pero necesito el recuento de * cada * personaje. – Elliot

+1

mi idea es mantener el diccionario de char -> contar pares y luego obtener el carácter en el índice dado e incrementar su conteo en el diccionario ... o simplemente podría iterar sobre la cadena y verificar si cada carácter está en su conjunto, si entonces es increment su conteo – stefanB

7

Cada vez que busca las cosas de una NSString, trate de usar NSScanner primero.

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

no he podido hacer que esto funcione en absoluto. Estoy tratando de contar la cantidad de espacios. – lawrence

+3

@lawrence Por defecto, los espacios y espacios en blanco son ignorados por NSScanner. – borrrden

+3

Puede llamar a 'setCharactersToBeSkipped: (NSCharacterSet *) skipSet' con' nil' como 'skipSet' - y' NSScanner' no saltará ningún carácter. –

1

El ejemplo con el escáner se colgó en el iPhone. Encontré esta solución:

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

Me funcionó sin fallas. Espero que pueda ayudar!

2

Su solución no funcionó para mí, he añadido una condición en el bucle para incrementar numberOfChar sólo si mainScanner ha llegado al final de la cadena:

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

Tenga en cuenta que esta es una solución rápida, no me No tiene tiempo para hacer una solución elegante ...

96

Puede hacerlo en una línea. Por ejemplo, este cuenta el número de espacios:

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

Crea una matriz temporal sin embargo .. Para la velocidad, se debe preferir [la respuesta de Jacque] (http://stackoverflow.com/a/15947190/482601). – fabian789

+1

¿Esto cuenta un espacio al principio y al final de 'yourString'? –

+0

La diferencia de velocidad solo importará si lo hace miles de veces. –

22

Pruebe esta categoría en 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 

Ésta es aproximadamente 5 veces más rápido que el enfoque componentsSeparatedByString:.

+0

Esta categoría funciona bien, pero tengo una pregunta: ¿puede usar tanto UniChar (desde CFString.h) como unichar (desde NSString) como si fueran lo mismo? – Bjinse

5

Hoy en día la primera cosa que viene a la mente para algo así: 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); 
} 

registros:

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

Aquí está una versión de trabajo Swift 3, por NSRange, Rango, Cadena y NSString! Disfrutar :)

/// 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 [] 
     } 
    } 
} 
Cuestiones relacionadas