web-dev-qa-db-fra.com

Comment utilisez-vous String.substringWithRange? (ou comment les gammes fonctionnent-elles à Swift?)

Je n'ai pas encore été capable de comprendre comment obtenir une sous-chaîne de String dans Swift:

var str = “Hello, playground”
func test(str: String) -> String {
 return str.substringWithRange( /* What goes here? */ )
}
test (str)

Je ne suis pas en mesure de créer une plage dans Swift. La saisie semi-automatique sur le terrain de jeu n’est pas très utile, c’est ce qu’elle suggère:

return str.substringWithRange(aRange: Range<String.Index>)

Je n'ai rien trouvé dans la bibliothèque de référence standard de Swift qui aide. Voici une autre supposition sauvage:

return str.substringWithRange(Range(0, 1))

Et ça:

let r:Range<String.Index> = Range<String.Index>(start: 0, end: 2)
return str.substringWithRange(r)

J'ai vu d'autres réponses ( Trouver l'index du caractère dans Swift String ) qui semblent suggérer que, puisque String est un type de pont pour NSString, les "anciennes" méthodes devraient fonctionner, mais on ne sait pas comment - par exemple, ceci ne fonctionne pas non plus (ne semble pas être une syntaxe valide):

let x = str.substringWithRange(NSMakeRange(0, 3))

Pensées?

251
Rob

Vous pouvez utiliser la méthode substringWithRange. Il faut un début et une fin String.Index.

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) //"Hello, playground"

Pour modifier les index de début et de fin, utilisez advancedBy (n).

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex.advancedBy(2), end: str.endIndex.advancedBy(-1))) //"llo, playgroun"

Vous pouvez également toujours utiliser la méthode NSString avec NSRange, mais vous devez vous assurer que vous utilisez un NSString comme ceci:

let myNSString = str as NSString
myNSString.substringWithRange(NSRange(location: 0, length: 3))

Remarque: comme JanX2 l'a mentionné, cette seconde méthode n'est pas sûre avec les chaînes unicode.

249
Connor

Swift 2

Simple

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

Swift 3

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)

str[startIndex...endIndex]       // "Strin"
str.substring(to: startIndex)    // "My "
str.substring(from: startIndex)  // "String"

Swift 4

substring(to:) et substring(from:) sont obsolètes en Swift 4.

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"
160
Warif Akhand Rishi

NOTE: @airspeedswift fait quelques des points très perspicaces sur les compromis de cette approche, en particulier les impacts cachés sur les performances. Les chaînes ne sont pas de simples animaux, et atteindre un index particulier peut prendre du temps O(n), ce qui signifie qu'une boucle utilisant un indice peut être O (n ^ 2). Tu étais prévenu.

Vous devez simplement ajouter une nouvelle fonction subscript qui prend une plage et utilise advancedBy() pour aller où vous voulez:

import Foundation

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let startIndex = self.startIndex.advancedBy(r.startIndex)
            let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

var s = "Hello, playground"

println(s[0...5]) // ==> "Hello,"
println(s[0..<5]) // ==> "Hello"

(Cela devrait certainement faire partie de la langue. Veuillez duper rdar: // 17158813 )

Pour le plaisir, vous pouvez également ajouter un opérateur + sur les index:

func +<T: ForwardIndex>(var index: T, var count: Int) -> T {
  for (; count > 0; --count) {
    index = index.succ()
  }
  return index
}

s.substringWithRange(s.startIndex+2 .. s.startIndex+5)

(Je ne sais pas encore si celui-ci devrait faire partie de la langue ou non.)

43
Rob Napier

Au moment où j'écris, aucune extension n'est parfaitement compatible Swift 4.2. En voici donc une qui couvre tous les besoins auxquels je peux penser:

extension String {
    func substring(from: Int?, to: Int?) -> String {
        if let start = from {
            guard start < self.count else {
                return ""
            }
        }

        if let end = to {
            guard end >= 0 else {
                return ""
            }
        }

        if let start = from, let end = to {
            guard end - start >= 0 else {
                return ""
            }
        }

        let startIndex: String.Index
        if let start = from, start >= 0 {
            startIndex = self.index(self.startIndex, offsetBy: start)
        } else {
            startIndex = self.startIndex
        }

        let endIndex: String.Index
        if let end = to, end >= 0, end < self.count {
            endIndex = self.index(self.startIndex, offsetBy: end + 1)
        } else {
            endIndex = self.endIndex
        }

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

    func substring(from: Int) -> String {
        return self.substring(from: from, to: nil)
    }

    func substring(to: Int) -> String {
        return self.substring(from: nil, to: to)
    }

    func substring(from: Int?, length: Int) -> String {
        guard length > 0 else {
            return ""
        }

        let end: Int
        if let start = from, start > 0 {
            end = start + length - 1
        } else {
            end = length - 1
        }

        return self.substring(from: from, to: end)
    }

    func substring(length: Int, to: Int?) -> String {
        guard let end = to, end > 0, length > 0 else {
            return ""
        }

        let start: Int
        if let end = to, end - length > 0 {
            start = end - length + 1
        } else {
            start = 0
        }

        return self.substring(from: start, to: to)
    }
}

Et puis, vous pouvez utiliser:

let string = "Hello,World!"

string.substring(from: 1, to: 7)getget: Ello,Wo

string.substring(to: 7)getget: Hello,Wo

string.substring(from: 3)getget: lo,World!

string.substring(from: 1, length: 4)getget vous: Ello

string.substring(length: 4, to: 7)getget: o,Wo

Mis à jour substring(from: Int?, length: Int) pour prendre en charge à partir de zéro.

38
winterized

Swift 2.0

simple:

let myString = "full text container"
let substring = myString[myString.startIndex..<myString.startIndex.advancedBy(3)] // prints: ful

Swift 3.0

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

Swift 4.0

Les opérations de sous-chaîne renvoient une instance du type Sous-chaîne au lieu de Chaîne.

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

// Convert the result to a String for long-term storage.
let newString = String(substring)
30
phnmnn

C'est beaucoup plus simple que n'importe quelle réponse, une fois que vous avez trouvé la bonne syntaxe.

Je veux enlever le [et]

let myString = "[ABCDEFGHI]"
let startIndex = advance(myString.startIndex, 1) //advance as much as you like
let endIndex = advance(myString.endIndex, -1)
let range = startIndex..<endIndex
let myNewString = myString.substringWithRange( range )

le résultat sera "ABCDEFGHI" les index startIndex et endIndex peuvent également être utilisés dans 

let mySubString = myString.substringFromIndex(startIndex)

etc!

PS: Comme indiqué dans les remarques, il y a quelques changements de syntaxe dans Swift 2, qui vient avec xcode 7 et iOS9!

S'il vous plaît regardez cette page

18
user1700737

Par exemple, pour trouver le prénom (jusqu'au premier espace) dans mon nom complet:

let name = "Joris Kluivers"

let start = name.startIndex
let end = find(name, " ")

if end {
    let firstName = name[start..end!]
} else {
    // no space found
}

start et end sont de type String.Index ici et sont utilisés pour créer un Range<String.Index> et utilisés dans l'accesseur en indice (si un espace est trouvé dans la chaîne d'origine).

Il est difficile de créer un String.Index directement à partir d'une position entière telle qu'utilisée dans le post d'ouverture. En effet, chaque caractère de mon nom aurait la même taille en octets. Mais les caractères utilisant des accents spéciaux dans d'autres langues auraient pu utiliser plusieurs octets supplémentaires (en fonction du codage utilisé). Alors, à quel octet le nombre entier devrait-il se référer?

Il est possible de créer un nouveau String.Index à partir d'un existant en utilisant les méthodes succ et pred qui permettront de s'assurer que le nombre d'octets correct est ignoré pour atteindre le prochain point de code du codage. Cependant, dans ce cas, il est plus facile de rechercher l'index du premier espace de la chaîne pour trouver l'index de fin.

16
Joris Kluivers

Puisque String est un type de pont pour NSString, les "anciennes" méthodes devraient fonctionner, mais il n'est pas clair comment - par exemple, cela ne fonctionne pas non plus (ne semble pas être une syntaxe valide):

 let x = str.substringWithRange(NSMakeRange(0, 3))

Pour moi, c'est la partie vraiment intéressante de votre question. String est ponté vers NSString, donc la plupart méthodes NSString do travaillent directement sur une chaîne. Vous pouvez les utiliser librement et sans réfléchir. Ainsi, par exemple, cela fonctionne exactement comme vous le souhaitiez:

// delete all spaces from Swift String stateName
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")

Mais, comme cela arrive souvent, "je travaille mon mojo mais ça ne marche pas pour toi." Vous venez de choisir l'un des rares cas où il existe un parallèle portant le même nom que la méthode Swift , et dans un cas comme celui-là, la méthode Swift masque la méthode Objective-C. Ainsi, lorsque vous dites str.substringWithRange, Swift pense que vous voulez parler de la méthode Swift plutôt que de la méthode NSString - et ensuite vous êtes insensé, car la méthode Swift attend un Range<String.Index>, et vous ne savez pas comment en créer un. 

La solution de facilité est d’empêcher Swift de prendre une telle ombre, en exprimant explicitement:

let x = (str as NSString).substringWithRange(NSMakeRange(0, 3))

Notez qu'aucun travail supplémentaire important n'est impliqué ici. "Cast" ne signifie pas "convertir"; the String est est effectivement une chaîne NSString. Nous disons simplement à Swift comment regarder cette variable pour les besoins de cette ligne de code.

La partie vraiment étrange de tout cela est que la méthode Swift, qui cause tout ce problème, est non documentée. Je ne sais pas où c'est défini; ce n'est pas dans l'en-tête NSString et ce n'est pas non plus dans l'en-tête Swift.

15
matt

Dans la nouvelle utilisation de Xcode 7.0

//: Playground - noun: a place where people can play

import UIKit

var name = "How do you use String.substringWithRange?"
let range = name.startIndex.advancedBy(0)..<name.startIndex.advancedBy(10)
name.substringWithRange(range)

//OUT:

 enter image description here

13
Claudio Guirunas

La réponse courte est que c'est vraiment difficile à Swift en ce moment. Mon impression est qu'il reste encore beaucoup de travail pour Apple à faire sur les méthodes pratiques pour des choses comme celle-ci.

String.substringWithRange() attend un paramètre Range<String.Index> et, autant que je sache, il n'existe pas de méthode de génération pour le type String.Index. Vous pouvez récupérer les valeurs String.Index à partir de aString.startIndex et aString.endIndex et appeler .succ() ou .pred(), mais c’est de la folie.

Que diriez-vous d’une extension de la classe String qui utilise de bons vieux Ints?

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let subStart = advance(self.startIndex, r.startIndex, self.endIndex)
            let subEnd = advance(subStart, r.endIndex - r.startIndex, self.endIndex)
            return self.substringWithRange(Range(start: subStart, end: subEnd))
        }
    }
    func substring(from: Int) -> String {
        let end = countElements(self)
        return self[from..<end]
    }
    func substring(from: Int, length: Int) -> String {
        let end = from + length
        return self[from..<end]
    }
}

let mobyDick = "Call me Ishmael."
println(mobyDick[8...14])             // Ishmael

let dogString = "This ????'s name is Patch."
println(dogString[5..<6])               // ????
println(dogString[5...5])              // ????
println(dogString.substring(5))        // ????'s name is Patch.
println(dogString.substring(5, length: 1))   // ????

Mise à jour: Swift beta 4 résout les problèmes ci-dessous!

En l'état actuel des versions 3 et antérieures, même les chaînes natives de Swift ont quelques problèmes avec la gestion des caractères Unicode. L'icône de chien ci-dessus a fonctionné, mais ce qui suit ne fonctionne pas:

let harderString = "1:1️⃣"
for character in harderString {
    println(character)
}

Sortie:

1
:
1
️
⃣
12
Nate Cook

Vous pouvez utiliser cette extension pour améliorer substringWithRange

Swift 2.3

extension String
{   
    func substringWithRange(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end))
        return self.substringWithRange(range)
    }

    func substringWithRange(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location))
        return self.substringWithRange(range)
    }
}

Swift 3

extension String
{
    func substring(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: end)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }

    func substring(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: start + location)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }
}

Usage:

let str = "Hello, playground"

let substring1 = str.substringWithRange(0, end: 5) //Hello
let substring2 = str.substringWithRange(7, location: 10) //playground
11
ChikabuZ

Exemple de code expliquant comment obtenir une sous-chaîne dans Swift 2.0  

(i) Sous-chaîne à partir de l'index de départ

Contribution:-

var str = "Swift is very powerful language!"
print(str)

str = str.substringToIndex(str.startIndex.advancedBy(5))
print(str)

Sortie:-

Swift is very powerful language!
Swift

(ii) Sous-chaîne d'un index particulier

Contribution:- 

var str = "Swift is very powerful language!"
print(str)

str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2))
print(str)

Sortie:-

Swift is very powerful language!
is

J'espère que cela vous aidera!

7
Umang Kothari

Solution facile avec peu de code.

Créez une extension qui inclut des sous-chaînes de base que presque toutes les autres langues ont:

extension String {
    func subString(start: Int, end: Int) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: start)
        let endIndex = self.index(startIndex, offsetBy: end)

        let finalString = self.substring(from: startIndex)
        return finalString.substring(to: endIndex)
    }
}

Il suffit d'appeler cela avec

someString.subString(start: 0, end: 6)
7
Oliver Dixon

Cela fonctionne dans mon terrain de jeu :) 

String(seq: Array(str)[2...4])
6
patrykens

Mise à jour pour Xcode 7. Ajout de l'extension de chaîne:

Utilisation:

var chuck: String = "Hello Chuck Norris"
chuck[6...11] // => Chuck

La mise en oeuvre:

extension String {

    /**
     Subscript to allow for quick String substrings ["Hello"][0...1] = "He"
     */
    subscript (r: Range<Int>) -> String {
        get {
            let start = self.startIndex.advancedBy(r.startIndex)
            let end = self.startIndex.advancedBy(r.endIndex - 1)
            return self.substringWithRange(start..<end)
        }
    }

}
6
Firo

Rob Napier avait déjà donné une réponse géniale en utilisant l'indice. Mais j’ai ressenti un inconvénient car il n’ya pas de vérification des conditions hors limites. Cela peut avoir tendance à tomber en panne. J'ai donc modifié l'extension et la voici

extension String {
    subscript (r: Range<Int>) -> String? { //Optional String as return value
        get {
            let stringCount = self.characters.count as Int
            //Check for out of boundary condition
            if (stringCount < r.endIndex) || (stringCount < r.startIndex){
                return nil
            }
            let startIndex = self.startIndex.advancedBy(r.startIndex)

            let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

Sortie ci-dessous

var str2 = "Hello, World"

var str3 = str2[0...5]
//Hello,
var str4 = str2[0..<5]
//Hello
var str5 = str2[0..<15]
//nil

Je suggère donc de toujours vérifier si le laisse

if let string = str[0...5]
{
    //Manipulate your string safely
}
4
iPrabu

essayez ceci dans la cour de récréation

var str:String = "Hello, playground"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))

cela vous donnera "Ello, p"

Cependant, là où cela devient vraiment intéressant, c'est que si vous faites le dernier index plus grand que la chaîne dans un terrain de jeu, il affichera toutes les chaînes que vous avez définies après str: o

Range () semble être une fonction générique, de sorte qu'il a besoin de connaître le type auquel elle fait face.

Vous devez également lui indiquer la chaîne qui vous intéresse dans les terrains de jeu, car elle semble contenir toutes les piqûres dans une séquence les unes après les autres avec leur nom de variable par la suite.

Alors

var str:String = "Hello, playground"

var str2:String = "I'm the next string"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,49))

donne "Ello, playground str Je suis la prochaine chaîne str2 "

fonctionne même si str2 est défini avec un let

:)

4
Peter Roberts

Dans Swift3

Par exemple: une variable "Duke James Thomas", nous devons obtenir "James". 

let name = "Duke James Thomas"
let range: Range<String.Index> = name.range(of:"James")!
let lastrange: Range<String.Index> = img.range(of:"Thomas")!
var middlename = name[range.lowerBound..<lstrange.lowerBound]
print (middlename)
3
Sandu

En prenant une page de Rob Napier, j'ai développé ces Common String Extensions , dont deux sont:

subscript (r: Range<Int>) -> String
{
    get {
        let startIndex = advance(self.startIndex, r.startIndex)
        let endIndex = advance(self.startIndex, r.endIndex - 1)

        return self[Range(start: startIndex, end: endIndex)]
    }
}

func subString(startIndex: Int, length: Int) -> String
{
    var start = advance(self.startIndex, startIndex)
    var end = advance(self.startIndex, startIndex + length)
    return self.substringWithRange(Range<String.Index>(start: start, end: end))
}

Usage:

"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"
2
Albert Bori

Voici comment obtenir une plage à partir d'une chaîne:

var str = "Hello, playground"

let startIndex = advance(str.startIndex, 1)
let endIndex = advance(startIndex, 8)
let range = startIndex..<endIndex
let substr = str[range] //"Ello, pl"

Le point clé est que vous transmettez une plage de valeurs de type String.Index (c'est ce que advance renvoie) au lieu d'entiers. 

La raison pour laquelle cela est nécessaire est que les chaînes dans Swift n’ont pas d’accès aléatoire (essentiellement à cause de la longueur variable des caractères Unicode). Vous ne pouvez pas non plus faire str[1]. String.Index est conçu pour fonctionner avec leur structure interne. 

Vous pouvez cependant créer une extension avec un indice qui le fait pour vous. Vous pouvez donc simplement passer une plage d’entiers (voir par exemple Réponse de Rob Napier ). 

2
Ixx

voici un exemple pour obtenir video-Id uniquement .i.e (6oL687G0Iso) à partir de l'URL complète dans Swift

let str = "https://www.youtube.com/watch?v=6oL687G0Iso&list=PLKmzL8Ib1gsT-5LN3V2h2H14wyBZTyvVL&index=2"
var arrSaprate = str.componentsSeparatedByString("v=")
let start = arrSaprate[1]
let rangeOfID = Range(start: start.startIndex,end:start.startIndex.advancedBy(11))
let substring = start[rangeOfID]
print(substring)
1
Hardik Thakkar
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

Échantillon complet, vous pouvez voir ici

1
Svitlana

Commencez par créer la plage, puis la sous-chaîne. Vous pouvez utiliser la syntaxe fromIndex..<toIndex comme suit:

let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)
1
Johannes

http://www.learnswiftonline.com/reference-guides/string-reference-guide-for-Swift/ montre que cela fonctionne bien

var str = "abcd"
str = str.substringToIndex(1)
0
swiftmb

Si vous avez une NSRange, le pontage à NSString fonctionne de manière transparente. Par exemple, je travaillais avec UITextFieldDelegate et je voulais rapidement calculer la nouvelle valeur de chaîne quand il me demandait si elle devait remplacer la plage. 

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
    println("Got new string: ", newString)
}
0
joslinm

Si vous ne vous souciez pas de la performance ... c'est probablement la solution la plus concise de Swift 4

extension String {
    subscript(range: CountableClosedRange<Int>) -> String {
        return enumerated().filter{$0.offset >= range.first! && $0.offset < range.last!}
            .reduce(""){$0 + String($1.element)}
    }
}

Cela vous permet de faire quelque chose comme ceci:

let myStr = "abcd"
myStr[0..<2] // produces "ab"
0
Jiachen Ren

Vous pouvez utiliser n’importe laquelle des méthodes de sous-chaîne dans une extension Swift String que j’ai écrite https://bit.ly/JString .

var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"
0
William Falcon

Simple extension pour String:

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}

Eh bien, j'ai eu le même problème et résolu avec la fonction "bridgeToObjectiveC ()":

var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!

Veuillez noter que dans l'exemple, substringWithRange, en association avec NSMakeRange, fait partie de la chaîne commençant à l'index 6 (caractère "W") et se terminant à l'index 6 + 6 positions (caractère "!")

À votre santé.

0
sittisal