Pourquoi avez-vous besoin de la gamme? Les chaînes Swift utilisent Range<String.Index>, mais parfois il est nécessaire de travailler avec NSStringet NSRange, donc un peu plus de contexte serait utile. - Mais jetez un œil à stackoverflow.com/questions/24092884/… .
Martin R
Réponses:
258
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.
Plages fermées: a...b
Cet opérateur de plage crée une plage Swift qui comprend à la fois un élément aet un élément b, même s'il bs'agit de la valeur maximale possible pour un type (comme Int.max). Il existe deux types différents de plages fermées: ClosedRangeet CountableClosedRange.
1. 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...3let myArray =["a","b","c","d","e"]
myArray[myRange]//["b","c","d"]
Cependant, a ClosedRangen'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 forboucle. Pour cela, vous avez besoin du CountableClosedRange.
2. CountableClosedRange
Ceci est similaire au dernier sauf que maintenant la plage peut également être itérée.
let myRange:CountableClosedRange=1...3let myArray =["a","b","c","d","e"]
myArray[myRange]// ["b", "c", "d"]
for index in myRange {
print(myArray[index])}
Gammes semi-ouvertes: a..<b
Cet opérateur de plage inclut un élément amais pas un élément b. Comme ci-dessus, il existe deux types différents de plages semi-ouvertes: Rangeet CountableRange.
1. Range
Comme avec ClosedRange, vous pouvez accéder aux éléments d'une collection avec un Range. Exemple:
let myRange:Range=1..<3let 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.
2. CountableRange
A CountableRangepermet l'itération.
let myRange:CountableRange=1..<3let myArray =["a","b","c","d","e"]
myArray[myRange]// ["b", "c"]
for index in myRange {
print(myArray[index])}
NSRange
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.
Gammes avec cordes
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 NSRangeet un NSStringavec 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"Whereis 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.
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.startIndexou 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
Vous ne pouvez pas utiliser une plage que vous avez créée avec une chaîne sur une chaîne différente.
Comme vous pouvez le voir, les plages de chaînes sont une douleur dans Swift, mais elles permettent de mieux gérer les emoji et autres scalaires Unicode.
Mon commentaire concerne la sous-section "Problème avec NSRange". NSStringstocke 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. NSRangecomptes 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
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)
Je vois qu'ils ont le type de données "Range". Alors, comment puis-je l'utiliser?
vichhai
@vichhai Pouvez-vous en dire plus là où vous souhaitez utiliser?
Dharmbir Singh
Comme dans l'objectif c: gamme NSRange;
vichhai
1
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 prefixet 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>:
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:
let nsRange =NSRange(location: someInt, length: someInt)
un péché
let myNSString = bigTOTPCode asNSString//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
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/… .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.Plages fermées:
a...b
Cet opérateur de plage crée une plage Swift qui comprend à la fois un élément
a
et un élémentb
, même s'ilb
s'agit de la valeur maximale possible pour un type (commeInt.max
). Il existe deux types différents de plages fermées:ClosedRange
etCountableClosedRange
.1.
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:
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 unefor
boucle. Pour cela, vous avez besoin duCountableClosedRange
.2.
CountableClosedRange
Ceci est similaire au dernier sauf que maintenant la plage peut également être itérée.
Gammes semi-ouvertes:
a..<b
Cet opérateur de plage inclut un élément
a
mais pas un élémentb
. Comme ci-dessus, il existe deux types différents de plages semi-ouvertes:Range
etCountableRange
.1.
Range
Comme avec
ClosedRange
, vous pouvez accéder aux éléments d'une collection avec unRange
. Exemple:Encore une fois, cependant, vous ne pouvez pas itérer sur un
Range
car il est seulement comparable, non stridable.2.
CountableRange
A
CountableRange
permet l'itération.NSRange
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.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.Gammes avec cordes
Les opérateurs de plage
...
et..<
sont un moyen abrégé de créer des plages. Par exemple:Le moyen le plus long de créer la même gamme serait
Vous pouvez voir que le type d'index ici est
Int
. Cela ne fonctionne pas pourString
, 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 unNSString
avec emoji et vous verrez ce que je veux dire. Mal de crâne.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>
, nonRange<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
Gammes unilatérales:
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 .
Chaîne
Cela fonctionne également avec les plages String. Si vous créez une plage avec
str.startIndex
oustr.endIndex
à une extrémité, vous pouvez la laisser désactivée. Le compilateur l'inférera.Donné
Vous pouvez passer de l'index à str.endIndex en utilisant
...
Voir également:
Remarques
Une étude plus approfondie
la source
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èresXcode 8 bêta 2 • Swift 3
Xcode 7 • Swift 2.0
ou simplement
la source
Retour...
la source
Utilisez comme ça
Sortie: Bonjour
la source
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
etsuffix
.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>
: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)"
estRange<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:On peut ainsi écrire un autre utilitaire où l'on passe directement d'un emplacement et d'une longueur String à un NSRange:
la source
Si quelqu'un veut créer un objet NSRange peut créer comme:
cela créera une plage avec la position 0 et la longueur 5
la source
la source
J'ai créé l'extension suivante:
exemple d'utilisation:
la source
Vous pouvez utiliser comme ça
un péché
la source
Je veux faire ça:
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:
Ajoutez simplement ceci à votre projet:
la source