2009-06-02 13 views
31

Mam NSString lub NSMutableString i chciałbym uzyskać liczbę wystąpień określonego znaku.Liczba wystąpień znaku w NSString

Muszę to zrobić dla kilku znaków - w tym przypadku wielkich angielskich znaków - więc byłoby miło, gdyby było szybkie.

Odpowiedz

1

I prawdopodobnie używać

NSString rangeOfCharacterFromSet:

lub

rangeOfCharacterFromSet:options:range::

gdzie zbiór jest zbiorem znaków szukasz. Zwraca z lokalizacją pierwszego znaku pasującego do zestawu. Zachowaj tablicę lub słownik i zwiększ liczbę znaków, a następnie powtórz.

+0

Jeśli dobrze rozumiem dokumentację, dałoby to zakres dowolnych znaków w zestawie. Ale potrzebuję liczby * każdego * znaku. – Elliot

+1

moim pomysłem jest utrzymanie słownika char -> liczba par, a następnie pobranie znaku w danym indeksie i zwiększenie jego liczby w słowniku ... lub po prostu powtórzenie ciągu znaków i sprawdzenie, czy każda postać jest w twoim zestawie, jeśli to jest przyrost, który się liczy – stefanB

7

Gdy szukasz rzeczy w NSString, spróbuj użyć NSScanner pierwszy.

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

Nie byłem w stanie w ogóle uruchomić tego. Próbuję policzyć liczbę spacji. – lawrence

+3

@lawrence Domyślnie spacje i białe spacje są ignorowane przez NSScanner. – borrrden

+3

Możesz wywołać 'setCharactersToBeSkipped: (NSCharacterSet *) skipSet' z' nil' jako 'skipSet' - a' NSScanner' nie pominie żadnych znaków. –

1

Przykład ze skanerem ulegał awarii na iPhonie. Znalazłem to rozwiązanie:

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

Udało mi się to bez awarii. Mam nadzieję, że to pomoże!

2

Twoje rozwiązanie nie działa dla mnie, dodałem warunek w pętli, aby zwiększyć numberOfChar tylko jeśli mainScanner osiągnie koniec łańcucha:

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

pamiętać, że jest szybkie ustalenie, I don nie ma czasu na zrobienie eleganckiego rozwiązania ...

96

Możesz to zrobić w jednym wierszu. Na przykład ten zlicza ilość miejsc:

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

Tworzy tymczasową tablicę. Aby uzyskać szybkość, należy preferować [odpowiedź Jacque'a] (http://stackoverflow.com/a/15947190/482601). – fabian789

+1

Czy to policzy spację na początku i końcu 'yourString'? –

+0

Różnica prędkości będzie miała znaczenie tylko wtedy, gdy robisz to tysiące razy. –

22

Spróbuj kategorię na 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 

ten jest około 5 razy szybciej niż podejście componentsSeparatedByString:.

+0

Ta kategoria działa dobrze, ale mam pytanie: czy możesz używać zarówno UniChar (z CFString.h), jak i unichar (z NSString) jako takich samych? – Bjinse

5

Obecnie pierwszą rzeczą, która przyjdzie mi do głowy coś takiego: 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); 
} 

kłody:

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

Oto Swift 3 wersja działa na NSRange, zakres, String i NSString! Ciesz się :)

/// 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 [] 
     } 
    } 
} 
Powiązane problemy