Je suis habitué à le faire en JavaScript:
var domains = "abcde".substring(0, "abcde".indexOf("cd")) // Returns "ab"
Swift n'a pas cette fonction, comment faire quelque chose de similaire?
Xcode 9 • Swift 4 ou version ultérieure
extension StringProtocol where Index == String.Index {
func index(of string: Self, options: String.CompareOptions = []) -> Index? {
return range(of: string, options: options)?.lowerBound
}
func endIndex(of string: Self, options: String.CompareOptions = []) -> Index? {
return range(of: string, options: options)?.upperBound
}
func indexes(of string: Self, options: String.CompareOptions = []) -> [Index] {
var result: [Index] = []
var start = startIndex
while start < endIndex,
let range = self[start..<endIndex].range(of: string, options: options) {
result.append(range.lowerBound)
start = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
func ranges(of string: Self, options: String.CompareOptions = []) -> [Range<Index>] {
var result: [Range<Index>] = []
var start = startIndex
while start < endIndex,
let range = self[start..<endIndex].range(of: string, options: options) {
result.append(range)
start = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
}
utilisation:
let str = "abcde"
if let index = str.index(of: "cd") {
let substring = str[..<index] // ab
let string = String(substring)
print(string) // "ab\n"
}
let str = "Hello, playground, playground, playground"
str.index(of: "play") // 7
str.endIndex(of: "play") // 11
str.indexes(of: "play") // [7, 19, 31]
str.ranges(of: "play") // [{lowerBound 7, upperBound 11}, {lowerBound 19, upperBound 23}, {lowerBound 31, upperBound 35}]
échantillon insensible à la casse
let query = "Play"
let ranges = str.ranges(of: query, options: .caseInsensitive)
let matches = ranges.map { str[$0] } //
print(matches) // ["play", "play", "play"]
échantillon d'expression régulière
let query = "play"
let escapedQuery = NSRegularExpression.escapedPattern(for: query)
let pattern = "\\b\(escapedQuery)\\w+" // matches any Word that starts with "play" prefix
let ranges = str.ranges(of: pattern, options: .regularExpression)
let matches = ranges.map { str[$0] }
print(matches) // ["playground", "playground", "playground"]
Testé pour Swift 4.2/4.1/4.0/3.0
En utilisantString[Range<String.Index>]
subscript, vous pouvez obtenir la sous-chaîne. Vous avez besoin de l'index de départ et du dernier index pour créer la plage et vous pouvez le faire comme ci-dessous
let str = "abcde"
if let range = str.range(of: "cd") {
let substring = str[..<range.lowerBound] // or str[str.startIndex..<range.lowerBound]
print(substring) // Prints ab
}
else {
print("String not present")
}
Si vous ne définissez pas l'index de démarrage de cet opérateur ..<
, il utilise l'index de départ. Vous pouvez également utiliser str[str.startIndex..<range.lowerBound]
au lieu de str[..<range.lowerBound]
let str = "abcdefghabcd"
if let index = str.index(of: "b") {
print(index) // Index(_compoundOffset: 4, _cache: Swift.String.Index._Cache.character(1))
}
let str : String = "ilike"
for i in 0...str.count {
let index = str.index(str.startIndex, offsetBy: i) // String.Index
let prefix = str[..<index] // String.SubSequence
let suffix = str[index...] // String.SubSequence
print("prefix \(prefix), suffix : \(suffix)")
}
prefix , suffix : ilike
prefix i, suffix : like
prefix il, suffix : ike
prefix ili, suffix : ke
prefix ilik, suffix : e
prefix ilike, suffix :
let substring1 = string[startIndex...endIndex] // including endIndex
let subString2 = string[startIndex..<endIndex] // excluding endIndex
Faire cela dans Swift est possible mais cela prend plus de lignes, voici une fonction indexOf()
faisant ce qui est attendu
func indexOf(source: String, substring: String) -> Int? {
let maxIndex = source.characters.count - substring.characters.count
for index in 0...maxIndex {
let rangeSubstring = source.startIndex.advancedBy(index)..<source.startIndex.advancedBy(index + substring.characters.count)
if source.substringWithRange(rangeSubstring) == substring {
return index
}
}
return nil
}
var str = "abcde"
if let indexOfCD = indexOf(str, substring: "cd") {
let distance = str.startIndex.advancedBy(indexOfCD)
print(str.substringToIndex(distance)) // Returns "ab"
}
Cette fonction n'est pas optimisée, mais elle fonctionne pour les chaînes courtes.
Dans la version 3 de Swift, String n’a pas les fonctions suivantes:
str.index(of: String)
Si l'index est requis pour une sous-chaîne, l'un des moyens consiste à obtenir la plage. Nous avons les fonctions suivantes dans la chaîne qui renvoie la plage -
str.range(of: <String>)
str.rangeOfCharacter(from: <CharacterSet>)
str.range(of: <String>, options: <String.CompareOptions>, range: <Range<String.Index>?>, locale: <Locale?>)
Par exemple pour trouver les index de première apparition de jeu dans str
var str = "play play play"
var range = str.range(of: "play")
range?.lowerBound //Result : 0
range?.upperBound //Result : 4
Remarque: la plage est facultative. S'il n'est pas capable de trouver la chaîne, il sera rendu nul. Par exemple
var str = "play play play"
var range = str.range(of: "Zoo") //Result : nil
range?.lowerBound //Result : nil
range?.upperBound //Result : nil
Il y a trois problèmes étroitement liés ici:
Toutes les méthodes de recherche de sous-chaîne sont terminées dans le monde Cocoa NSString (Foundation)
Foundation NSRange ne correspond pas à Swift Range; la première utilise le début et la longueur, la dernière utilise les extrémités
En général, les caractères Swift sont indexés en utilisant String.Index
, pas Int, mais les caractères Foundation sont indexés en utilisant Int, et il n'y a pas de traduction directe simple entre eux (car Foundation et Swift ont des conceptions différentes de ce qui constitue un caractère)
Compte tenu de tout cela, réfléchissons à la façon d'écrire:
func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
// ?
}
La sous-chaîne s2
doit être recherchée dans s
à l'aide d'une méthode String Foundation. La plage résultante nous revient, pas sous la forme d'une NSRange (même s'il s'agit d'une méthode Foundation), mais sous la forme d'une plage de String.Index
(entourée d'une option, au cas où nous n'aurions pas trouvé la sous-chaîne du tout) . Cependant, l'autre nombre, from
, est un Int. Ainsi, nous ne pouvons former aucune sorte de gamme impliquant les deux.
Mais nous n'avons pas à le faire! Tout ce que nous avons à faire est de couper le end de notre chaîne originale en utilisant une méthode qui prend un String.Index
, et de couper le début de notre chaîne originale en utilisant une méthode qui prend un Int. Heureusement, de telles méthodes existent! Comme ça:
func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
guard let r = s.range(of:s2) else {return nil}
var s = s.prefix(upTo:r.lowerBound)
s = s.dropFirst(from)
return s
}
Ou, si vous préférez pouvoir appliquer cette méthode directement à une chaîne, comme ceci ...
let output = "abcde".substring(from:0, toSubstring:"cd")
... puis en faire une extension sur String:
extension String {
func substring(from:Int, toSubstring s2 : String) -> Substring? {
guard let r = self.range(of:s2) else {return nil}
var s = self.prefix(upTo:r.lowerBound)
s = s.dropFirst(from)
return s
}
}