web-dev-qa-db-fra.com

Comment créer une plage dans Swift?

En Objective-c, nous créons une plage en utilisant NSRange

NSRange range;

Alors, comment créer une gamme dans Swift?

94
vichhai

mis à jour pour Swift 4

Les gammes Swift sont plus complexes que NSRange et ne sont pas devenues plus simples dans Swift 3. Si vous voulez essayer de comprendre le raisonnement qui sous-tend 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 comprenant les deux éléments a et élément b, même si b est la valeur maximale possible pour un type (comme Int.max). Il existe deux types de plages fermées: ClosedRange et 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...3

let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]

Cependant, un ClosedRange n'est pas comptable (c'est-à-dire qu'il n'est pas conforme au protocole de séquence). Cela signifie que vous ne pouvez pas parcourir les éléments avec une boucle for. Pour cela, vous avez besoin de la CountableClosedRange.

2. 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])
}

Gammes mi-ouvertes: a..<b

Cet opérateur de plage inclut l'élément a mais et non l'élément b. Comme ci-dessus, il existe deux types de plages semi-ouvertes: Range et CountableRange.

1. 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 parcourir un Range car il est seulement comparable, pas stridable.

2. CountableRange

Un 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])
}

NSRange

Vous pouvez (devez) toujours utiliser NSRange parfois 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 d'emplacement et de longueur et non d'index de début et d'index de fin. L'exemple présenté ici a une signification similaire à la plage Swift _ 3..<5. Cependant, étant donné que les types sont différents, ils ne sont pas interchangeables.

Gammes avec des cordes

Les opérateurs de plage ... et ..< constituent un raccourci pour créer des plages. Par exemple:

let myRange = 1..<3

Le moyen long pour 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, car les chaînes sont constituées de caractères et tous les caractères ne sont pas de la même taille. (Lire this pour plus d'informations.) Un emoji comme ???? prend par exemple 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 tête.

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 smiley prend deux unités de code UTF-16 à stocker, ce qui donne le résultat inattendu de ne pas inclure le "d".

Solution rapide

Pour cette raison, avec Swift Chaînes, vous utilisez Range<String.Index>, pas Range<Int>. L'indice de chaîne est calculé en fonction d'une chaîne particulière afin qu'il sache s'il existe des grappes d'emoji ou 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"

Plages unilatérales: a... et ...b et ..<b

Dans Swift 4, les choses ont été simplifiées un peu. Chaque fois que le point de départ ou d'arrivée d'une plage peut être déduit, vous pouvez le laisser.

Int

Vous pouvez utiliser des plages entières unilatérales pour parcourir des 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 aussi avec les gammes 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

Voir également:

Notes

  • 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 constater, les plages de chaînes sont pénibles dans Swift, mais elles permettent de mieux gérer les emoji et les autres scalaires Unicode.

Une étude plus approfondie

228
Suragch

Xcode 8 beta 2 • Swift

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.

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
25
Leo Dabus

(1 .. <10)

résultats...

Plage = 1 .. <10

3
Victor Lin

Utiliser 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

2
Dharmbir Singh

Si quelqu'un veut créer un objet NSRange, il peut créer:

let range: NSRange = NSRange.init(location: 0, length: 5)

cela va créer une plage avec la position 0 et la longueur 5

1
Van

Je trouve surprenant que, même dans Swift 4, il n’existe toujours pas de moyen natif simple d’exprimer une plage de chaînes à l’aide de Int. Les seules méthodes String permettant de fournir un int pour obtenir une sous-chaîne par plage sont prefix et suffix.

Il est utile de disposer d’utilitaires de conversion pour pouvoir parler comme NSRange lorsqu’on parle à une chaîne. Voici un utilitaire qui prend un emplacement et une longueur, comme NSRange, et retourne 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 le Range<String.Index> englobant le premier caractère de "hello". En bonus, j'ai autorisé les emplacements négatifs: "hello".range(-1,1)" est le Range<String.Index> englobant le dernier caractère de "hello".

Il est également utile de convertir un Range<String.Index> en un NSRange, pour les moments où vous devez parler à Cocoa (par exemple, lorsque vous traitez avec des plages d'attributs NSAttributedString). Swift 4 fournit un moyen natif de le faire:

let nsrange = NSRange(range, in:s) // where s is the string

Nous pouvons donc écrire un autre utilitaire où nous allons directement d’un emplacement String et d’une longueur à un NSRange:

extension String {
    func nsRange(_ start:Int, _ length:Int) -> NSRange {
        return NSRange(self.range(start,length), in:self)
    }
}
1
matt

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
0
dr OX

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
0
Mohammad Nurdin
func replace(input: String, start: Int,lenght: Int, newChar: Character) -> String {
var chars = Array(input.characters)

for i in start...lenght {
    guard i < input.characters.count else{
        break
    }
    chars[i] = newChar
}
return String(chars)

}

0
Abo3atef