Réponses:
Mis à jour pour Swift 4
Les gammes Swift sont plus complexes que NSRange
, et elles ne sont pas devenues plus faciles dans Swift 3. Si vous voulez essayer de comprendre le raisonnement derrière une partie de cette complexité, lisez ceci et ceci . Je vais juste vous montrer comment les créer et quand vous pourriez les utiliser.
a...b
Cet opérateur de plage crée une plage Swift qui comprend à la fois un élément a
et un élément b
, même s'il b
s'agit de la valeur maximale possible pour un type (comme Int.max
). Il existe deux types différents de plages fermées: ClosedRange
et CountableClosedRange
.
ClosedRange
Les éléments de toutes les plages de Swift sont comparables (c'est-à-dire qu'ils sont conformes au protocole Comparable). Cela vous permet d'accéder aux éléments de la plage à partir d'une collection. Voici un exemple:
let myRange: ClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
Cependant, a ClosedRange
n'est pas dénombrable (c'est-à-dire qu'il n'est pas conforme au protocole Sequence). Cela signifie que vous ne pouvez pas parcourir les éléments avec une for
boucle. Pour cela, vous avez besoin du CountableClosedRange
.
CountableClosedRange
Ceci est similaire au dernier sauf que maintenant la plage peut également être itérée.
let myRange: CountableClosedRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
for index in myRange {
print(myArray[index])
}
a..<b
Cet opérateur de plage inclut un élément a
mais pas un élément b
. Comme ci-dessus, il existe deux types différents de plages semi-ouvertes: Range
et CountableRange
.
Range
Comme avec ClosedRange
, vous pouvez accéder aux éléments d'une collection avec un Range
. Exemple:
let myRange: Range = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
Encore une fois, cependant, vous ne pouvez pas itérer sur un Range
car il est seulement comparable, non stridable.
CountableRange
A CountableRange
permet l'itération.
let myRange: CountableRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
for index in myRange {
print(myArray[index])
}
Vous pouvez (devez) toujours l'utiliser NSRange
à certains moments dans Swift (lors de la création de chaînes attribuées , par exemple), il est donc utile de savoir comment en créer une.
let myNSRange = NSRange(location: 3, length: 2)
Notez qu'il s'agit de l'emplacement et de la longueur , et non de l'index de début et de l'index de fin. L'exemple ici est similaire dans sa signification à la gamme Swift 3..<5
. Cependant, comme les types sont différents, ils ne sont pas interchangeables.
Les opérateurs de plage ...
et ..<
sont un moyen abrégé de créer des plages. Par exemple:
let myRange = 1..<3
Le moyen le plus long de créer la même gamme serait
let myRange = CountableRange<Int>(uncheckedBounds: (lower: 1, upper: 3)) // 1..<3
Vous pouvez voir que le type d'index ici est Int
. Cela ne fonctionne pas pour String
, cependant, car les chaînes sont constituées de caractères et tous les caractères n'ont pas la même taille. (Lisez ceci pour plus d'informations.) Un emoji comme 😀, par exemple, prend plus d'espace que la lettre «b».
Problème avec NSRange
Essayez d'expérimenter avec NSRange
et un NSString
avec emoji et vous verrez ce que je veux dire. Mal de crâne.
let myNSRange = NSRange(location: 1, length: 3)
let myNSString: NSString = "abcde"
myNSString.substring(with: myNSRange) // "bcd"
let myNSString2: NSString = "a😀cde"
myNSString2.substring(with: myNSRange) // "😀c" Where is the "d"!?
Le visage souriant prend deux unités de code UTF-16 pour stocker, donc il donne le résultat inattendu de ne pas inclure le "d".
Solution rapide
Pour cette raison, avec Swift Strings que vous utilisez Range<String.Index>
, non Range<Int>
. L'index de chaîne est calculé en fonction d'une chaîne particulière afin qu'il sache s'il existe des emoji ou des grappes de graphèmes étendus.
Exemple
var myString = "abcde"
let start = myString.index(myString.startIndex, offsetBy: 1)
let end = myString.index(myString.startIndex, offsetBy: 4)
let myRange = start..<end
myString[myRange] // "bcd"
myString = "a😀cde"
let start2 = myString.index(myString.startIndex, offsetBy: 1)
let end2 = myString.index(myString.startIndex, offsetBy: 4)
let myRange2 = start2..<end2
myString[myRange2] // "😀cd"
a...
et...b
et..<b
Dans Swift 4, les choses ont été un peu simplifiées. Chaque fois que le point de départ ou de fin d'une plage peut être déduit, vous pouvez le laisser désactivé.
Int
Vous pouvez utiliser des plages d'entiers à un côté pour parcourir les collections. Voici quelques exemples tirés de la documentation .
// iterate from index 2 to the end of the array
for name in names[2...] {
print(name)
}
// iterate from the beginning of the array to index 2
for name in names[...2] {
print(name)
}
// iterate from the beginning of the array up to but not including index 2
for name in names[..<2] {
print(name)
}
// the range from negative infinity to 5. You can't iterate forward
// over this because the starting point in unknown.
let range = ...5
range.contains(7) // false
range.contains(4) // true
range.contains(-1) // true
// You can iterate over this but it will be an infinate loop
// so you have to break out at some point.
let range = 5...
Chaîne
Cela fonctionne également avec les plages String. Si vous créez une plage avec str.startIndex
ou str.endIndex
à une extrémité, vous pouvez la laisser désactivée. Le compilateur l'inférera.
Donné
var str = "Hello, playground"
let index = str.index(str.startIndex, offsetBy: 5)
let myRange = ..<index // Hello
Vous pouvez passer de l'index à str.endIndex en utilisant ...
var str = "Hello, playground"
let index = str.index(str.endIndex, offsetBy: -10)
let myRange = index... // playground
Remarques
NSString
stocke en interne ses caractères en encodage UTF-16. Un scalaire Unicode complet est de 21 bits. Le caractère de visage souriant ( U+1F600
) ne peut pas être stocké dans une seule unité de code 16 bits, il est donc réparti sur 2. NSRange
comptes basés sur des unités de code 16 bits. Dans cet exemple, 3 unités de code ne représentent que 2 caractères
Xcode 8 bêta 2 • Swift 3
let myString = "Hello World"
let myRange = myString.startIndex..<myString.index(myString.startIndex, offsetBy: 5)
let mySubString = myString.substring(with: myRange) // Hello
Xcode 7 • Swift 2.0
let myString = "Hello World"
let myRange = Range<String.Index>(start: myString.startIndex, end: myString.startIndex.advancedBy(5))
let mySubString = myString.substringWithRange(myRange) // Hello
ou simplement
let myString = "Hello World"
let myRange = myString.startIndex..<myString.startIndex.advancedBy(5)
let mySubString = myString.substringWithRange(myRange) // Hello
Utilisez comme ça
var start = str.startIndex // Start at the string's start index
var end = advance(str.startIndex, 5) // Take start index and advance 5 characters forward
var range: Range<String.Index> = Range<String.Index>(start: start,end: end)
let firstFiveDigit = str.substringWithRange(range)
print(firstFiveDigit)
Sortie: Bonjour
Je trouve surprenant que, même dans Swift 4, il n'y ait toujours pas de moyen natif simple d'exprimer une plage String en utilisant Int. Les seules méthodes String qui vous permettent de fournir un Int comme moyen d'obtenir une sous-chaîne par plage sont prefix
et suffix
.
Il est utile d'avoir sous la main des utilitaires de conversion, afin que nous puissions parler comme NSRange lorsque nous parlons à une chaîne. Voici un utilitaire qui prend un emplacement et une longueur, tout comme NSRange, et renvoie un Range<String.Index>
:
func range(_ start:Int, _ length:Int) -> Range<String.Index> {
let i = self.index(start >= 0 ? self.startIndex : self.endIndex,
offsetBy: start)
let j = self.index(i, offsetBy: length)
return i..<j
}
Par exemple, "hello".range(0,1)"
est-ce que l' Range<String.Index>
embrassement est le premier caractère de "hello"
. En prime, j'ai autorisé les emplacements négatifs: "hello".range(-1,1)"
est Range<String.Index>
le dernier personnage de "hello"
.
Il est également utile de convertir a Range<String.Index>
en NSRange, pour les moments où vous devez parler à Cocoa (par exemple, pour traiter les plages d'attributs NSAttributedString). Swift 4 fournit un moyen natif de le faire:
let nsrange = NSRange(range, in:s) // where s is the string
On peut ainsi écrire un autre utilitaire où l'on passe directement d'un emplacement et d'une longueur String à un NSRange:
extension String {
func nsRange(_ start:Int, _ length:Int) -> NSRange {
return NSRange(self.range(start,length), in:self)
}
}
J'ai créé l'extension suivante:
extension String {
func substring(from from:Int, to:Int) -> String? {
if from<to && from>=0 && to<self.characters.count {
let rng = self.startIndex.advancedBy(from)..<self.startIndex.advancedBy(to)
return self.substringWithRange(rng)
} else {
return nil
}
}
}
exemple d'utilisation:
print("abcde".substring(from: 1, to: 10)) //nil
print("abcde".substring(from: 2, to: 4)) //Optional("cd")
print("abcde".substring(from: 1, to: 0)) //nil
print("abcde".substring(from: 1, to: 1)) //nil
print("abcde".substring(from: -1, to: 1)) //nil
Vous pouvez utiliser comme ça
let nsRange = NSRange(location: someInt, length: someInt)
un péché
let myNSString = bigTOTPCode as NSString //12345678
let firstDigit = myNSString.substringWithRange(NSRange(location: 0, length: 1)) //1
let secondDigit = myNSString.substringWithRange(NSRange(location: 1, length: 1)) //2
let thirdDigit = myNSString.substringWithRange(NSRange(location: 2, length: 4)) //3456
Je veux faire ça:
print("Hello"[1...3])
// out: Error
Mais malheureusement, je ne peux pas écrire moi-même un indice parce que celui qui est détesté occupe l'espace de nom.
Nous pouvons faire ceci cependant:
print("Hello"[range: 1...3])
// out: ell
Ajoutez simplement ceci à votre projet:
extension String {
subscript(range: ClosedRange<Int>) -> String {
get {
let start = String.Index(utf16Offset: range.lowerBound, in: self)
let end = String.Index(utf16Offset: range.upperBound, in: self)
return String(self[start...end])
}
}
}
Range<String.Index>
, mais parfois il est nécessaire de travailler avecNSString
etNSRange
, donc un peu plus de contexte serait utile. - Mais jetez un œil à stackoverflow.com/questions/24092884/… .