2016-12-29 1 views
-1

Ainsi, la façon dont je reçois l'instance d'une chaîne dans une autre chaîne est en utilisant cette extension de classe String:Vérifier instance non-première de chaîne dans la chaîne

func indexOf(target: String) -> Int? { 

     let range = (self as NSString).range(of: target) 

     guard range.toRange() != nil else { 
      return nil 
     } 

     return range.location 

    } 

Cependant, maintenant je cherche un moyen d'obtenir la nième instance d'une chaîne dans une chaîne. Par exemple, si je voulais obtenir l'index de la 3ème instance de "Maison" dans "Maisons sur le spectacle Maison maison des gens étranges.". Comment ferais-je cela?

+1

Pourquoi utilisez-vous NSString? La chaîne a des méthodes similaires. – rmaddy

+0

Je n'ai pas trouvé une méthode String qui a accompli la tâche de trouver une chaîne dans une chaîne, seulement un caractère dans une chaîne @maddy –

+0

_pour obtenir l'index de la 3ème instance de "House" dans "Maisons sur le salon House house strange people . "_ Il n'y a pas de troisième instance. '" House "' et '" house "' sont deux chaînes différentes. – matt

Répondre

2

Une solution qui fonctionne directement sur la Swift String, sans pont à NSString (et sans créer de sous-chaînes temporaires):

extension String { 

    func index(of target: String, instance: Int = 1) -> Int? { 
     precondition(!target.isEmpty) 
     precondition(instance > 0) 

     var found = 0 // Number of occurrences found so far. 
     var pos = startIndex // Current search position. 
     // Search for next occurrence of `target`. 
     while let r = range(of: target, range: pos..<endIndex) { 
      found += 1 
      // Are we done? 
      if found == instance { 
       // Distance in # of characters: 
       return distance(from: startIndex, to: r.lowerBound) 
      } 
      // Continue search after this occurrence. 
      pos = r.upperBound 
     } 
     return nil 
    } 
} 

Exemple:

let bStr = "Houses on the show Dr. House's PlayHouse house strange people" 
let ls = "House" 

if let idx = bStr.index(of: ls, instance: 3) { 
    print(idx) // 35 
} 
+0

"sans passer à NSString" Vous ne diffusez pas explicitement vers NSString, mais en appelant des appels range (de: range:) 'dans Foundation, donc il y a certainement un pont - c'est juste implicite. Je ne critique pas la mise en œuvre, je conteste la revendication. – matt

+0

@matt: Vous avez raison bien sûr. Ce que je voulais dire, c'est le pont explicite et le saut entre les indices String et NSString. La seule raison de convertir la gamme finale en un Int était parce que cela semblait être ce que OP recherche. –

1

J'ai aimé la réponse de Martin R mais Je trouve une expression récursive plus intuitive. Dans ma mise en œuvre, nous revenons en fait la chaîne (en option):

extension String { 
    typealias SRange = Range<String.Index> 
    func range(of target:String, options:String.CompareOptions, nth:Int) -> SRange? { 
     func helper(hnth:Int, range:SRange) -> SRange? { 
      let r = self.range(of: target, options: options, range: range) 
      if let r = r, hnth < nth { 
       return helper(hnth:hnth+1, range:r.upperBound..<self.endIndex) 
      } 
      return r 
     } 
     return helper(hnth:1, range: self.startIndex..<self.endIndex) 
    } 
} 

qui retourne une Swift Range si l'événement nth de la chaîne cible existe, nil autrement. Voici un test:

let s = "Houses on the show House house strange people." 
let targ = "House" 
let r = s.range(of: targ, options: .caseInsensitive, nth: 3) 
// now r is an Optional wrapping 25..<30 
+0

N'a jamais pensé à utiliser une approche récursive. Ceci est une réponse très avancée et complète. Merci de partager cela avec moi. –