2015-07-31 17 views
21

Mój główny ciąg to "hello Swift Swift and Swift", a podłańcem jest Swift. Potrzebuję uzyskać liczbę wystąpień podłańcucha "Swift" we wspomnianym łańcuchu.Swift :: Liczba wystąpień podciągu w łańcuchu

Ten kod może określać, czy wzór istnieje. var String = "hello Swift Swift i Swift"

if string.rangeOfString("Swift") != nil{ 
println("exists") 
} 

Teraz muszę znać numer wystąpienia.

Odpowiedz

49

Prostym rozwiązaniem byłoby podzielić na "Swift" i odjąć 1 od liczby części:

let s = "hello Swift Swift and Swift" 
let tok = s.components(separatedBy:"Swift") 
print(tok.count-1) 

Ten kod drukuje 3.

Edit: Przed Swift 3 syntax kod wyglądał tak :

let tok = s.componentsSeparatedByString("Swift") 
+0

Cześć, jak wiele wiele innych odpowiedzi wskazać, czy ciąg rozpoczyna się od "Swift", ten kod nie będzie działać. (off przez jeden błąd) Próbuję uzyskać prostą implementację do tego również, ale na razie nadal nie. Jeśli znajdę coś tak czystego (bez forów i innych masywnych metod), opublikuję odpowiedź. – PedroSilva

+5

PedroSilva, to nie jest poprawne. Oto przykład: '" Swift ".componentsSeparatedByString (" Swift "). Count' wyświetla" 2 ". Zatem 'count - 1' wydrukuje liczbę wystąpień, w tym przypadku' 1'. –

10

Polecam przedłużenie sznurka w Swift 3, takie jak:

extension String { 
    func countInstances(of stringToFind: String) -> Int { 
     var stringToSearch = self 
     var count = 0 
     while let foundRange = stringToSearch.range(of: stringToFind, options: .diacriticInsensitive) { 
      stringToSearch = stringToSearch.replacingCharacters(in: foundRange, with: "") 
      count += 1 
     } 
     return count 
    } 
} 

Jest to pętla, która znajduje i usuwa każdą instancję stringToFind, zwiększając liczbę przy każdej kolejce. Po tym, jak searchString nie zawiera już stringToFind, pętla zrywa się i licznik powraca.

Zauważ, że używam .diacriticInsensitive, więc ignoruje akcenty (na przykład oboje i résume zostaną znalezieni). Możesz dodać lub zmienić opcje w zależności od typów ciągów, które chcesz znaleźć.

+1

'replaceSubrange' może być lepsze niż' replaceingCharacters', aby uniknąć tworzenia nowego ciągu za każdym razem. Ponadto użycie 'range (of: options: range:)' może być lepsze niż 'range (of: options:)', aby za każdym razem uniknąć wyszukiwania za pomocą startIndex. –

+0

Zastosowałem opisane optymalizacje w [moja odpowiedź] (https://stackoverflow.com/a/45073012/1033581). –

2

Potrzebowałem sposobu na zliczanie podciągów, które mogą zawierać początek następnego dopasowanego podciągu. Wykorzystując dwsolbergs rozszerzenie i zakres Strings (z: Opcje: zakres: locale :) metoda wymyśliłem tego rozszerzenia String

extension String 
{ 
    /** 
    Counts the occurrences of a given substring by calling Strings `range(of:options:range:locale:)` method multiple times. 

    - Parameter substring : The string to search for, optional for convenience 

    - Parameter allowOverlap : Bool flag indicating whether the matched substrings may overlap. Count of "" in "" is 2 if allowOverlap is **false**, and 3 if it is **true** 

    - Parameter options : String compare-options to use while counting 

    - Parameter range : An optional range to limit the search, default is **nil**, meaning search whole string 

    - Parameter locale : Locale to use while counting 

    - Returns : The number of occurrences of the substring in this String 
    */ 
    public func count(
     occurrencesOf substring: String?, 
     allowOverlap: Bool = false, 
     options: String.CompareOptions = [], 
     range searchRange: Range<String.Index>? = nil, 
     locale: Locale? = nil) -> Int 
    { 
     guard let substring = substring, !substring.isEmpty else { return 0 } 

     var count = 0 

     let searchRange = searchRange ?? startIndex..<endIndex 

     var searchStartIndex = searchRange.lowerBound 
     let searchEndIndex = searchRange.upperBound 

     while let rangeFound = range(of: substring, options: options, range: searchStartIndex..<searchEndIndex, locale: locale) 
     { 
      count += 1 

      if allowOverlap 
      { 
       searchStartIndex = index(rangeFound.lowerBound, offsetBy: 1) 
      } 
      else 
      { 
       searchStartIndex = rangeFound.upperBound 
      } 
     } 

     return count 
    } 
} 
6

Optymalizacja dwsolbergs solution liczyć szybciej. Również szybciej niż componentsSeparatedByString.

extension String { 
    func countInstances(of stringToFind: String) -> Int { 
     assert(!stringToFind.isEmpty) 
     var searchRange: Range<String.Index>? 
     var count = 0 
     while let foundRange = range(of: stringToFind, options: .diacriticInsensitive, range: searchRange) { 
      searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex)) 
      count += 1 
     } 
     return count 
    } 
} 

Zastosowanie:

// return 2 
"aaaa".countInstances(of: "aa") 
+1

Bardzo ładne rozwiązanie, zwłaszcza, że ​​zmieniając opcje na '[.caseInsensitive,.diacriticInsensitive] "może znaleźć szukany termin bez względu na duże/małe litery. Brawo! – ConfusionTowers

0

Spróbuj

var mainString = "hello Swift Swift and Swift" 
var count = 0 

mainString.enumerateSubstrings(in: mainString.startIndex..<mainString.endIndex, options: .byWords) { (subString, subStringRange, enclosingRange, stop) in 

    if case let s? = subString{ 

     if s.caseInsensitiveCompare("swift") == .orderedSame{ 
      count += 1 
     } 
    } 


} 

print(count) 
Powiązane problemy