web-dev-qa-db-fra.com

Obtenir le nième caractère d'une chaîne dans le langage de programmation Swift

Comment puis-je obtenir le nième caractère d'une chaîne? J'ai essayé l'accesseur de support ([]) sans succès.

var string = "Hello, world!"

var firstChar = string[0] // Throws error

ERREUR: 'indice' n'est pas disponible: impossible d'indiquer une chaîne avec un int, voir le commentaire sur la documentation pour en savoir plus

381
Mohsen

Attention: Voir réponse de Leo Dabus pour une implémentation correcte de Swift 4.

Swift 4

Le type Substring a été introduit dans Swift 4 pour rendre les sous-chaînes plus rapides et plus efficaces en partageant le stockage avec la chaîne d'origine. C'est donc ce que les fonctions en indice doivent renvoyer.

Essayez-le ici

extension String {
  subscript (i: Int) -> Character {
    return self[index(startIndex, offsetBy: i)]
  }
  subscript (bounds: CountableRange<Int>) -> Substring {
    let start = index(startIndex, offsetBy: bounds.lowerBound)
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[start ..< end]
  }
  subscript (bounds: CountableClosedRange<Int>) -> Substring {
    let start = index(startIndex, offsetBy: bounds.lowerBound)
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[start ... end]
  }
  subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
    let start = index(startIndex, offsetBy: bounds.lowerBound)
    let end = index(endIndex, offsetBy: -1)
    return self[start ... end]
  }
  subscript (bounds: PartialRangeThrough<Int>) -> Substring {
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[startIndex ... end]
  }
  subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[startIndex ..< end]
  }
}
extension Substring {
  subscript (i: Int) -> Character {
    return self[index(startIndex, offsetBy: i)]
  }
  subscript (bounds: CountableRange<Int>) -> Substring {
    let start = index(startIndex, offsetBy: bounds.lowerBound)
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[start ..< end]
  }
  subscript (bounds: CountableClosedRange<Int>) -> Substring {
    let start = index(startIndex, offsetBy: bounds.lowerBound)
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[start ... end]
  }
  subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
    let start = index(startIndex, offsetBy: bounds.lowerBound)
    let end = index(endIndex, offsetBy: -1)
    return self[start ... end]
  }
  subscript (bounds: PartialRangeThrough<Int>) -> Substring {
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[startIndex ... end]
  }
  subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
    let end = index(startIndex, offsetBy: bounds.upperBound)
    return self[startIndex ..< end]
  }
}

Pour convertir la Substring en String, vous pouvez simplement faire String(string[0..2]), mais vous ne devriez le faire que si vous prévoyez de conserver la sous-chaîne. Sinon, il est plus efficace de garder un Substring.

Ce serait formidable si quelqu'un pouvait trouver un bon moyen de fusionner ces deux extensions en une seule. J'ai essayé d'étendre StringProtocol sans succès, car la méthode index n'existe pas.

Swift 3:

extension String {
  subscript (i: Int) -> Character {
    return self[index(startIndex, offsetBy: i)]
  }
  subscript (i: Int) -> String {
    return String(self[i] as Character)
  }
  subscript (r: Range<Int>) -> String {
    let start = index(startIndex, offsetBy: r.lowerBound)
    let end = index(startIndex, offsetBy: r.upperBound)
    return self[Range(start ..< end)]
  }
}

Pourquoi n'est-ce pas intégré?

Apple fournit l'explication suivante ( trouvé ici ):

La souscription de chaînes avec des entiers n'est pas disponible.

Le concept de "caractère ie dans une chaîne" a différentes interprétations dans différentes bibliothèques et composants système. L'interprétation correcte doit être sélectionnée en fonction du cas d'utilisation et des API impliquées. Ainsi, String ne peut pas être indexé avec un entier.

Swift propose plusieurs manières d'accéder aux données de caractère stockées dans des chaînes.

  • String.utf8 est une collection d'unités de code UTF-8 dans la chaîne. Utilisez cette API lors de la conversion de la chaîne en UTF-8. La plupart des API POSIX traitent les chaînes en termes d'unités de code UTF-8.

  • String.utf16 est une collection d'unités de code UTF-16 dans une chaîne. La plupart des API Touch et Cocoa traitent les chaînes en termes d'unités de code UTF-16. Par exemple, les instances de NSRange utilisées avec NSAttributedString et NSRegularExpression stockent des décalages et des longueurs de sous-chaîne en termes d'unités de code UTF-16.

  • String.unicodeScalars est une collection de scalaires Unicode. Utilisez cette API lorsque vous effectuez une manipulation de bas niveau des données de caractères.

  • String.characters est une collection de grappes de graphèmes étendues, qui constituent une approximation des caractères perçus par l'utilisateur.

Notez que lors du traitement de chaînes contenant du texte lisible par l'homme, le traitement caractère par caractère doit être évité dans toute la mesure du possible. Utilisez à la place des algorithmes Unicode sensibles aux paramètres régionaux, par exemple, String.localizedStandardCompare(), String.localizedLowercaseString, String.localizedStandardRangeOfString() etc.

541
aleclarson

Swift 5 et plus tôt

let str = "abcdef"
str[1 ..< 3] // returns "bc"
str[5] // returns "f"
str[80] // returns ""
str.substring(fromIndex: 3) // returns "def"
str.substring(toIndex: str.length - 2) // returns "abcd"

Vous devrez ajouter cette extension String à votre projet (elle est entièrement testée):

extension String {

  var length: Int {
    return count
  }

  subscript (i: Int) -> String {
    return self[i ..< i + 1]
  }

  func substring(fromIndex: Int) -> String {
    return self[min(fromIndex, length) ..< length]
  }

  func substring(toIndex: Int) -> String {
    return self[0 ..< max(0, toIndex)]
  }

  subscript (r: Range<Int>) -> String {
    let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                        upper: min(length, max(0, r.upperBound))))
    let start = index(startIndex, offsetBy: range.lowerBound)
    let end = index(start, offsetBy: range.upperBound - range.lowerBound)
    return String(self[start ..< end])
  }

}

Même si Swift avait toujours une solution immédiate à ce problème (sans l'extension String, que j'ai fournie ci-dessous), je le ferais quand même fortement recommandé en utilisant l'extension. Pourquoi? Parce que cela m'a évité des dizaines de heures de migration pénible depuis les premières versions de Swift, où la syntaxe de String changeait presque chaque version, mais tout ce que j'avais à faire était de mettre à jour l'implémentation de l'extension plutôt que de refactoriser l'ensemble du projet. Fais ton choix.

let str = "Hello, world!"
let index = str.index(str.startIndex, offsetBy: 4)
str[index] // returns Character 'o'

let endIndex = str.index(str.endIndex, offsetBy:-2)
str[index ..< endIndex] // returns String "o, worl"

String(str.suffix(from: index)) // returns String "o, world!"
String(str.prefix(upTo: index)) // returns String "Hell"
310
NAlexN

Je viens de trouver cette solution de contournement soignée

var firstChar = Array(string)[0]
129
Jens Wirth

Pas d'indexation utilisant des entiers, uniquement avec String.Index. Principalement avec une complexité linéaire. Vous pouvez également créer des plages à partir de String.Index et obtenir des sous-chaînes les utilisant.

Swift 3.

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.index(before: someString.endIndex)]
let charAtIndex = someString[someString.index(someString.startIndex, offsetBy: 10)]

let range = someString.startIndex..<someString.index(someString.startIndex, offsetBy: 10)
let substring = someString[range]

Swift 2.x

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.endIndex.predecessor()]
let charAtIndex = someString[someString.startIndex.advanceBy(10)]

let range = someString.startIndex..<someString.startIndex.advanceBy(10)
let subtring = someString[range]

Notez que vous ne pouvez jamais utiliser un index (ou une plage) créé d'une chaîne à une autre.

let index10 = someString.startIndex.advanceBy(10)

//will compile
//sometimes it will work but sometimes it will crash or result in undefined behaviour
let charFromAnotherString = anotherString[index10]
122
Sulthan

Xcode 10.2 • Swift 5

Vous pouvez étendre StringProtocol pour que l'indice soit également disponible pour les sous-chaînes:

extension StringProtocol {        
    subscript(offset: Int) -> Element {
        return self[index(startIndex, offsetBy: offset)]
    }
    subscript(_ range: Range<Int>) -> SubSequence {
        return prefix(range.lowerBound + range.count)
            .suffix(range.count)
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        return prefix(range.lowerBound + range.count)
            .suffix(range.count)
    }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence {
        return prefix(range.upperBound.advanced(by: 1))
    }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence {
        return prefix(range.upperBound)
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence {
        return suffix(Swift.max(0, count - range.lowerBound))
    }
}

extension LosslessStringConvertible {
    var string: String { return .init(self) }
}

extension BidirectionalCollection {
    subscript(safe offset: Int) -> Element? {
        guard !isEmpty, let i = index(startIndex, offsetBy: offset, limitedBy: index(before: endIndex)) else { return nil }
        return self[i]
    }
}

Essai

let test = "Hello USA ????????!!! Hello Brazil ????????!!!"
test[safe: 10]   // "????????"
test[11]   // "!"
test[10...]   // "????????!!! Hello Brazil ????????!!!"
test[10..<12]   // "????????!"
test[10...12]   // "????????!!"
test[...10]   // "Hello USA ????????"
test[..<10]   // "Hello USA "
test.first   // "H"
test.last    // "!"

// Subscripting the Substring
 test[...][...3]  // "Hell"

// Note that they all return a Substring of the original String.
// To create a new String you need to add .string as follow
test[10...].string  // "????????!!! Hello Brazil ????????!!!"
103
Leo Dabus

Swift 4

let str = "My String"

Chaîne à l'index

let index = str.index(str.startIndex, offsetBy: 3)
String(str[index])    // "S"

Substring

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)
String(str[startIndex...endIndex])     // "Strin"

premiers n caractères

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[..<startIndex])    // "My "

n derniers caractères

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[startIndex...])    // "String"

Swift 2 et 3

str = "My String"

** Chaîne à l'index **

Swift 2

let charAtIndex = String(str[str.startIndex.advancedBy(3)])  // charAtIndex = "S"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)]

Sous-chaîne fromIndex toIndex

Swift 2

let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)] // subStr = "Strin"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)...str.index(str.startIndex, offsetBy: 7)]

n premiers caractères

let first2Chars = String(str.characters.prefix(2)) // first2Chars = "My"

Derniers n caractères

let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"
59
Warif Akhand Rishi

Swift 2.0 à partir de Xcode 7 GM Graine

var text = "Hello, world!"

let firstChar = text[text.startIndex.advancedBy(0)] // "H"

Pour le nième caractère, remplacez 0 par n-1.

Édition: Swift 3.

text[text.index(text.startIndex, offsetBy: 0)]


n.b. il existe des moyens plus simples de saisir certains caractères dans la chaîne

par exemple. let firstChar = text.characters.first

24
Matt Le Fleur

Si vous voyez Cannot subscript a value of type 'String'... utilisez cette extension:

Swift 3

extension String {
    subscript (i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    subscript (i: Int) -> String {
        return String(self[i] as Character)
    }

    subscript (r: Range<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start..<end]
    }

    subscript (r: ClosedRange<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start...end]
    }
}

Swift 2.3

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = advance(startIndex, integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = advance(startIndex, integerRange.startIndex)
        let end = advance(startIndex, integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}

Source: http://oleb.net/blog/2014/07/Swift-strings/

23
SoftDesigner

Solution Swift 2.2:

L'extension suivante fonctionne dans Xcode 7, c'est une combinaison de this solution et de la conversion de syntaxe Swift 2.0.

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = startIndex.advancedBy(integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = startIndex.advancedBy(integerRange.startIndex)
        let end = startIndex.advancedBy(integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}
19
Dan Beaulieu

Swift 4

String(Array(stringToIndex)[index]) 

C’est probablement le meilleur moyen de résoudre ce problème une fois pour toutes. Vous voudrez probablement d'abord convertir la chaîne en tant que tableau, puis à nouveau le résultat en tant que chaîne. Sinon, un caractère sera renvoyé au lieu d'une chaîne.

Exemple String(Array("HelloThere")[1]) retournera "e" sous forme de chaîne.

(Array("HelloThere")[1] retournera "e" en tant que caractère.

Swift n'autorise pas l'indexation des chaînes comme des tableaux, mais le travail est fait dans le style brute-force.

11
Harsh G.

En guise de remarque, il existe quelques fonctions directement applicables à la représentation d'une chaîne de caractères, comme ceci:

var string = "Hello, playground"
let firstCharacter = string.characters.first // returns "H"
let lastCharacter = string.characters.last // returns "d"

Le résultat est de type Caractère, mais vous pouvez le convertir en chaîne.

Ou ca:

let reversedString = String(string.characters.reverse())
// returns "dnuorgyalp ,olleH" 

:-)

11
Frédéric Adda

La classe de chaînes Swift ne permet pas d'obtenir un caractère à un index spécifique en raison de sa prise en charge native des caractères UTF. La longueur variable d'un caractère UTF en mémoire rend impossible l'accès direct à un caractère. Cela signifie que vous devez boucler manuellement la chaîne à chaque fois.

Vous pouvez étendre String pour fournir une méthode permettant de parcourir les caractères jusqu'à l'index souhaité.

extension String {
    func characterAtIndex(index: Int) -> Character? {
        var cur = 0
        for char in self {
            if cur == index {
                return char
            }
            cur++
        }
        return nil
    }
}

myString.characterAtIndex(0)!
11
drewag

Je viens d'avoir le même problème. Faites simplement ceci:

var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)
5
user3723247

Ma solution très simple:

let myString = "Test string"
let index = 0
let firstCharacter = myString[String.Index(encodedOffset: index)]
4
Linh Dao

Swift3

Vous pouvez utiliser la syntaxe en indice pour accéder au caractère à un index de chaîne particulier.

let greeting = "Guten Tag!"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a

Visiter https://developer.Apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

ou nous pouvons faire une extension de chaîne dans Swift 4

extension String {
    func getCharAtIndex(_ index: Int) -> Character {
        return self[self.index(self.startIndex, offsetBy: index)]
    }
}

USAGE:

let foo = "ABC123"
foo.getCharAtIndex(2) //C
3
Hamed

Swift 3: une autre solution (testée dans un terrain de jeu)

extension String {
    func substr(_ start:Int, length:Int=0) -> String? {
        guard start > -1 else {
            return nil
        }

        let count = self.characters.count - 1

        guard start <= count else {
            return nil
        }

        let startOffset = max(0, start)
        let endOffset = length > 0 ? min(count, startOffset + length - 1) : count

        return self[self.index(self.startIndex, offsetBy: startOffset)...self.index(self.startIndex, offsetBy: endOffset)]
    }
}

Usage:

let txt = "12345"

txt.substr(-1) //nil
txt.substr(0) //"12345"
txt.substr(0, length: 0) //"12345"
txt.substr(1) //"2345"
txt.substr(2) //"345"
txt.substr(3) //"45"
txt.substr(4) //"5"
txt.substr(6) //nil
txt.substr(0, length: 1) //"1"
txt.substr(1, length: 1) //"2"
txt.substr(2, length: 1) //"3"
txt.substr(3, length: 1) //"4"
txt.substr(3, length: 2) //"45"
txt.substr(3, length: 3) //"45"
txt.substr(4, length: 1) //"5"
txt.substr(4, length: 2) //"5"
txt.substr(5, length: 1) //nil
txt.substr(5, length: -1) //nil
txt.substr(-1, length: -1) //nil
3
Peter Kreinz

Ma solution est dans une ligne, en supposant que cadena est la chaîne et 4 est la nième position que vous voulez:

let character = cadena[advance(cadena.startIndex, 4)]

Simple ... Je suppose que Swift inclura plus de choses sur les sous-chaînes dans les versions futures.

Swift 4

Souscription de plage partielle et partielle à l'aide de la propriété String de indices

Comme variante de @ LeoDabus Bonne réponse , nous pouvons ajouter une extension supplémentaire à DefaultBidirectionalIndices dans le but de nous permettre de nous rabattre sur la propriété indices DE String lors de l'implémentation des indices personnalisés (par Int plages spécialisées et partielles) pour le dernier.

extension DefaultBidirectionalIndices {
    subscript(at: Int) -> Elements.Index {
        return index(startIndex, offsetBy: at)
    }
}

// Moving the index(_:offsetBy:) to an extension yields slightly
// briefer implementations for these String extensions.
extension String {
    subscript(r: CountableClosedRange<Int>) -> SubSequence {
        return self[indices[r.lowerBound]...indices[r.upperBound]]
    }
    subscript(r: CountablePartialRangeFrom<Int>) -> SubSequence {
        return self[indices[r.lowerBound]...]
    }
    subscript(r: PartialRangeThrough<Int>) -> SubSequence {
        return self[...indices[r.upperBound]]
    }
    subscript(r: PartialRangeUpTo<Int>) -> SubSequence {
        return self[..<indices[r.upperBound]]
    }
}

let str = "foo bar baz bax"
print(str[4...6]) // "bar"
print(str[4...])  // "bar baz bax"
print(str[...6])  // "foo bar"
print(str[..<6])  // "foo ba"

Merci à @LeoDabus de m'avoir indiqué l'utilisation de la propriété indices comme (autre) alternative à l'indice String!

Swift 4.2.

Dans Swift 4.2, DefaultBidirectionalIndices est déconseillé en faveur de DefaultIndices .

2
dfri

Obtenir et définir un indice (chaîne et sous-chaîne) - Swift 4.2

Swift 4.2, Xcode 10

J'ai basé ma réponse sur la réponse de @ alecarlson . La seule grande différence est que vous pouvez obtenir un Substring ou un String renvoyé (et dans certains cas, un seul Character). Vous pouvez aussi get et set l'indice. Enfin, le mien est un peu plus lourd et plus long que @alecarlson la réponse et en tant que telle, je vous suggère de la mettre dans un fichier source.


Extension:

public extension String {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            self.replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
public extension Substring {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }

    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }

    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }

    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
2
Noah Wilder

Afin de nourrir le sujet et d'afficher les possibilités de Swift indice, voici une petite chaîne basée sur l'indice "substring-toolbox"

Ces méthodes sont sûres et ne vont jamais sur les index de chaînes

extension String {
    // string[i] -> one string char
    subscript(pos: Int) -> String { return String(Array(self)[min(self.length-1,max(0,pos))]) }

    // string[pos,len] -> substring from pos for len chars on the left
    subscript(pos: Int, len: Int) -> String { return self[pos, len, .pos_len, .left2right] }

    // string[pos, len, .right2left] -> substring from pos for len chars on the right
    subscript(pos: Int, len: Int, way: Way) -> String { return self[pos, len, .pos_len, way] }

    // string[range] -> substring form start pos on the left to end pos on the right
    subscript(range: Range<Int>) -> String { return self[range.startIndex, range.endIndex, .start_end, .left2right] }

    // string[range, .right2left] -> substring start pos on the right to end pos on the left
    subscript(range: Range<Int>, way: Way) -> String { return self[range.startIndex, range.endIndex, .start_end, way] }

    var length: Int { return countElements(self) }
    enum Mode { case pos_len, start_end }
    enum Way { case left2right, right2left }
    subscript(var val1: Int, var val2: Int, mode: Mode, way: Way) -> String {
        if mode == .start_end {
            if val1 > val2 { let val=val1 ; val1=val2 ; val2=val }
            val2 = val2-val1
        }
        if way == .left2right {
            val1 = min(self.length-1, max(0,val1))
            val2 = min(self.length-val1, max(1,val2))
        } else {
            let val1_ = val1
            val1 = min(self.length-1, max(0, self.length-val1_-val2 ))
            val2 = max(1, (self.length-1-val1_)-(val1-1) )
        }
        return self.bridgeToObjectiveC().substringWithRange(NSMakeRange(val1, val2))

        //-- Alternative code without bridge --
        //var range: Range<Int> = pos...(pos+len-1)
        //var start = advance(startIndex, range.startIndex)
        //var end = advance(startIndex, range.endIndex)
        //return self.substringWithRange(Range(start: start, end: end))
    }
}


println("0123456789"[3]) // return "3"

println("0123456789"[3,2]) // return "34"

println("0123456789"[3,2,.right2left]) // return "56"

println("0123456789"[5,10,.pos_len,.left2right]) // return "56789"

println("0123456789"[8,120,.pos_len,.right2left]) // return "01"

println("0123456789"[120,120,.pos_len,.left2right]) // return "9"

println("0123456789"[0...4]) // return "01234"

println("0123456789"[0..4]) // return "0123"

println("0123456789"[0...4,.right2left]) // return "56789"

println("0123456789"[4...0,.right2left]) // return "678" << because ??? range can wear endIndex at 0 ???
2
Luc-Olivier

Swift 4.2

Cette réponse est idéale car elle étend String et l’ensemble de ses Subsequences (Substring) dans une extension.

public extension StringProtocol {

    public subscript (i: Int) -> Element {
        return self[index(startIndex, offsetBy: i)]
    }

    public subscript (bounds: CountableClosedRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start...end]
    }

    public subscript (bounds: CountableRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start..<end]
    }

    public subscript (bounds: PartialRangeUpTo<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex..<end]
    }

    public subscript (bounds: PartialRangeThrough<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex...end]
    }

    public subscript (bounds: CountablePartialRangeFrom<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        return self[start..<endIndex]
    }
}

Usage

var str = "Hello, playground"

print(str[5...][...5][0])
// Prints ","
2
Noah Wilder

Mise à jour pour la sous-chaîne Swift 2.0

public extension String {
    public subscript (i: Int) -> String {
        return self.substringWithRange(self.startIndex..<self.startIndex.advancedBy(i + 1))
    }

    public subscript (r: Range<Int>) -> String {
        get {
            return self.substringWithRange(self.startIndex.advancedBy(r.startIndex)..<self.startIndex.advancedBy(r.endIndex))
        }
    }

}
2
YannSteph

Dans Swift

    let mystring = "Hello, world!"
    let stringToArray = Array(mystring.characters)

    let indices = (stringToArray.count)-1

    print(stringToArray[0]) //H
    print(stringToArray[indices]) //!
2
Rajamohan S

Swift 3

extension String {

    public func charAt(_ i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    public subscript (i: Int) -> String {
        return String(self.charAt(i) as Character)
    }

    public subscript (r: Range<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

    public subscript (r: CountableClosedRange<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

}

Utilisation

let str = "Hello World"
let sub = str[0...4]

Trucs et astuces utiles pour la programmation (écrit par moi)

2
quemeful

Je pense qu'une réponse rapide pour obtenir le premier caractère pourrait être:

let firstCharacter = aString[aString.startIndex]

C'est tellement élégant et performant que:

let firstCharacter = Array(aString.characters).first

Mais .. si vous voulez manipuler et faire plus d'opérations avec des chaînes, vous pourriez penser créer une extension .. il y a une extension avec cette approche, elle est assez similaire à celle déjà publiée ici:

extension String {
var length : Int {
    return self.characters.count
}

subscript(integerIndex: Int) -> Character {
    let index = startIndex.advancedBy(integerIndex)
    return self[index]
}

subscript(integerRange: Range<Int>) -> String {
    let start = startIndex.advancedBy(integerRange.startIndex)
    let end = startIndex.advancedBy(integerRange.endIndex)
    let range = start..<end
    return self[range]
}

}

MAIS C’EST UNE IDÉE TERRIBLE !!

L'extension ci-dessous est horriblement inefficace. Chaque fois qu'une chaîne est accédée avec un entier, une fonction O(n) pour faire avancer son index de départ est exécutée. Exécuter une boucle linéaire à l'intérieur d'une autre boucle linéaire signifie que cette boucle for est accidentellement O(n2) - à mesure que la longueur de la chaîne augmente, le temps que prend cette boucle augmente quadratiquement.

Au lieu de cela, vous pouvez utiliser la collection de chaînes de caractères.

2
ignaciohugog

Vous pouvez également convertir String en tableau de caractères comme celui-ci:

let text = "My Text"
let index = 2
let charSequence = text.unicodeScalars.map{ Character($0) }
let char = charSequence[index]

C'est le moyen d'obtenir un caractère à un index spécifié en temps constant.

L'exemple ci-dessous ne s'exécute pas en temps constant, mais nécessite un temps linéaire. Donc, si vous avez beaucoup de recherches dans String by index, utilisez la méthode ci-dessus.

let char = text[text.startIndex.advancedBy(index)]
1
Marcin Kapusta

Le type String de Swift ne fournit pas de méthode characterAtIndex car il existe plusieurs manières de coder une chaîne Unicode. Allez-vous avec UTF8, UTF16 ou autre chose?

Vous pouvez accéder aux collections CodeUnit en récupérant les propriétés String.utf8 et String.utf16. Vous pouvez également accéder à la collection UnicodeScalar en récupérant la propriété String.unicodeScalars.

Dans l'esprit de l'implémentation de NSString, je retourne un type unichar.

extension String
{
    func characterAtIndex(index:Int) -> unichar
    {
        return self.utf16[index]
    }

    // Allows us to use String[index] notation
    subscript(index:Int) -> unichar
    {
        return characterAtIndex(index)
    }
}

let text = "Hello Swift!"
let firstChar = text[0]
1
Erik

Dans Swift 3 sans extensions de la classe String, aussi simple que je puisse le faire!

let myString = "abcedfg"
let characterLocationIndex = myString.index(myString.startIndex, offsetBy: 3)
let myCharacter = myString[characterLocationIndex]

myCharacter est "3" dans cet exemple.

1
John Pavley

L'utilisation de caractères ferait l'affaire. Vous pouvez rapidement convertir la chaîne en un tableau de caractères pouvant être manipulé par les méthodes CharacterView.

Exemple:

let myString = "Hello World!"
let myChars  = myString.characters

(complet CharacterView doc)

(testé dans Swift 3)

1
Stephane Paquet

Il existe une alternative, expliquée dans manifeste de chaînes

extension String : BidirectionalCollection {
    subscript(i: Index) -> Character { return characters[i] }
}
1
George Maisuradze

Une solution de type python, qui vous permet d’utiliser un index négatif,

var str = "Hello world!"
str[-1]        // "!"

pourrait être:

extension String {
    subscript (var index:Int)->Character{
        get {
            let n = distance(self.startIndex, self.endIndex)
            index %= n
            if index < 0 { index += n }
            return self[advance(startIndex, index)]
        }
    }
}

À propos, il peut être intéressant de transposer le tout notation slice de python

1

Voici une extension que vous pouvez utiliser en travaillant avec Swift 3.1. Un seul index retournera un Character, ce qui semble intuitif lors de l'indexation d'un String, et un Range retournera un String.

extension String {
    subscript (i: Int) -> Character {
        return Array(self.characters)[i]
    }

    subscript (r: CountableClosedRange<Int>) -> String {
        return String(Array(self.characters)[r])
    }

    subscript (r: CountableRange<Int>) -> String {
        return self[r.lowerBound...r.upperBound-1]
    }
}

Quelques exemples de l'extension en action:

let string = "Hello"

let c1 = string[1]  // Character "e"
let c2 = string[-1] // fatal error: Index out of range

let r1 = string[1..<4] // String "ell"
let r2 = string[1...4] // String "Ello"
let r3 = string[1...5] // fatal error: Array index is out of range



nb Vous pouvez ajouter une méthode supplémentaire à l'extension ci-dessus pour renvoyer un String avec un seul caractère si vous le souhaitez:

subscript (i: Int) -> String {
    return String(self[i])
}

Notez que vous devrez alors spécifier explicitement le type souhaité lors de l'indexation de la chaîne:

let c: Character = string[3] // Character "l"
let s: String = string[0]    // String "H"
1
Matt Le Fleur

Vérifiez que c'est Swift 4

let myString = "LOVE"

self.textField1.text = String(Array(myString)[0])
self.textField2.text = String(Array(myString)[1])
self.textField3.text = String(Array(myString)[2])
self.textField4.text = String(Array(myString)[3])
0
Sreekanth G

permet les indices négatifs

Il est toujours utile de ne pas toujours avoir à écrire string[string.length - 1] pour obtenir le dernier caractère lors de l'utilisation d'une extension en indice. Cette extension (Swift) autorise les index négatifs, Range et CountableClosedRange.

extension String {
    var count: Int { return self.characters.count }

    subscript (i: Int) -> Character {
        // wraps out of bounds indices
        let j = i % self.count
        // wraps negative indices
        let x = j < 0 ? j + self.count : j

        // quick exit for first
        guard x != 0 else {
            return self.characters.first!
        }

        // quick exit for last
        guard x != count - 1 else {
            return self.characters.last!
        }

        return self[self.index(self.startIndex, offsetBy: x)]
    }

    subscript (r: Range<Int>) -> String {
        let lb = r.lowerBound
        let ub = r.upperBound

        // quick exit for one character
        guard lb != ub else { return String(self[lb]) }

        return self[self.index(self.startIndex, offsetBy: lb)..<self.index(self.startIndex, offsetBy: ub)]
    }

    subscript (r: CountableClosedRange<Int>) -> String {
        return self[r.lowerBound..<r.upperBound + 1]
    }
}

Comment vous pouvez l'utiliser:

var text = "Hello World"

text[-1]    // d
text[2]     // l
text[12]    // e
text[0...4] // Hello
text[0..<4] // Hell

Pour le programmeur plus approfondi: Incluez un guard contre les chaînes vides dans cette extension

subscript (i: Int) -> Character {
    guard self.count != 0 else { return '' }
    ...
}

subscript (r: Range<Int>) -> String {
    guard self.count != 0 else { return "" }
    ...
}
0
Jay Harris

Inclure cette extension dans votre projet

  extension String{
func trim() -> String
{
    return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
}

var length: Int {
    return self.count
}

subscript (i: Int) -> String {
    return self[i ..< i + 1]
}

func substring(fromIndex: Int) -> String {
    return self[min(fromIndex, length) ..< length]
}

func substring(toIndex: Int) -> String {
    return self[0 ..< max(0, toIndex)]
}

subscript (r: Range<Int>) -> String {
    let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                        upper: min(length, max(0, r.upperBound))))
    let start = index(startIndex, offsetBy: range.lowerBound)
    let end = index(start, offsetBy: range.upperBound - range.lowerBound)
    return String(self[start ..< end])
}

func substring(fromIndex: Int, toIndex:Int)->String{
    let startIndex = self.index(self.startIndex, offsetBy: fromIndex)
    let endIndex = self.index(startIndex, offsetBy: toIndex-fromIndex)

    return String(self[startIndex...endIndex])
}

Puis utilisez la fonction comme ceci

let str = "Sample-String"

let substring = str.substring(fromIndex: 0, toIndex: 0) //returns S
let sampleSubstr = str.substring(fromIndex: 0, toIndex: 5) //returns Sample
0
AkilRajdho

Obtenez la première lettre:

first(str) // retrieve first letter

Plus ici: http://sketchytech.blogspot.com/2014/08/Swift-pure-Swift-method-for-returning.html

0
philippinedev

Swift 3:

extension String {
    func substring(fromPosition: UInt, toPosition: UInt) -> String? {
        guard fromPosition <= toPosition else {
            return nil
        }

        guard toPosition < UInt(characters.count) else {
            return nil
        }

        let start = index(startIndex, offsetBy: String.IndexDistance(fromPosition))
        let end   = index(startIndex, offsetBy: String.IndexDistance(toPosition) + 1)
        let range = start..<end

        return substring(with: range)
    }
}

"ffaabbcc".substring(fromPosition: 2, toPosition: 5) // return "aabb"
0
Daniele Ceglia

Vous pouvez utiliser SwiftString ( https://github.com/amayne/SwiftString ) pour le faire.

"Hello, world!"[0] // H
"Hello, world!"[0...4] // Hello

AVERTISSEMENT: j'ai écrit cette extension

0
eurobrew

Je voulais souligner que si vous avez une grande chaîne et que vous devez accéder de manière aléatoire à de nombreux caractères, vous voudrez peut-être payer le coût supplémentaire en mémoire et convertir la chaîne en tableau pour une meilleure performance:

// Pay up front for O(N) memory
let chars = Array(veryLargeString.characters)

for i in 0...veryLargeNumber {
    // Benefit from O(1) access
    print(chars[i])
}
0
Joseph Chen