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
Attention: Voir réponse de Leo Dabus pour une implémentation correcte de 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.
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.
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)]
}
}
Apple fournit l'explication suivante ( trouvé ici ):
La souscription de chaînes avec des entiers n'est pas disponible.
Le concept de "caractère
i
e 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 deNSRange
utilisées avecNSAttributedString
etNSRegularExpression
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.
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"
Je viens de trouver cette solution de contournement soignée
var firstChar = Array(string)[0]
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]
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 ????????!!!"
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"
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"
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
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]
}
}
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]
}
}
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.
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"
:-)
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)!
Je viens d'avoir le même problème. Faites simplement ceci:
var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)
Ma solution très simple:
let myString = "Test string"
let index = 0
let firstCharacter = myString[String.Index(encodedOffset: index)]
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
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
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
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.
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
!
Dans Swift 4.2, DefaultBidirectionalIndices
est déconseillé en faveur de DefaultIndices
.
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.
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)")
}
}
}
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 ???
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]
}
}
var str = "Hello, playground"
print(str[5...][...5][0])
// Prints ","
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))
}
}
}
Dans Swift
let mystring = "Hello, world!"
let stringToArray = Array(mystring.characters)
let indices = (stringToArray.count)-1
print(stringToArray[0]) //H
print(stringToArray[indices]) //!
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)
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.
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)]
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]
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.
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)
Il existe une alternative, expliquée dans manifeste de chaînes
extension String : BidirectionalCollection {
subscript(i: Index) -> Character { return characters[i] }
}
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
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"
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])
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 "" }
...
}
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
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
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"
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
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])
}