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

266

Je n'ai pas encore pu comprendre comment obtenir une sous-chaîne d'un Stringdans 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 gamme dans Swift. La saisie semi-automatique dans Playground 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 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 de caractère dans la chaîne Swift ) qui semblent suggérer que puisqu'il Strings'agit d'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))

Pensées?

Rob
la source
Que voulez-vous dire lorsque vous dites que le dernier exemple ne fonctionne pas? Cela provoque-t-il une erreur ou renvoie-t-il une valeur inattendue?
67cherries
Veuillez dupe rdar: // 17158813 demandant la notation en indice openradar.appspot.com/radar?id=6373877630369792
Rob Napier

Réponses:

259

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 l'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 utiliser la méthode NSString avec NSRange, mais vous devez vous assurer que vous utilisez une NSString comme celle-ci:

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

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

Connor
la source
5
Ouais. Pour l'instant, je continuerais de me connecter à NSString si nécessaire. Il semble que la chaîne de Swift soit encore incomplète
Connor
14
Ce n'est pas sûr! Vous supposez que les index dans String et NSString sont interchangeables. Ce n'est pas le cas. Les index dans une chaîne se réfèrent aux points de code Unicode tandis que les index dans une chaîne NSString se réfèrent aux unités de code UTF-16! Essayez-le avec des emoji.
JanX2
3
Veuillez noter que les index dans une chaîne ne font plus référence aux points de code Unicode. Considérez-les comme faisant référence à des personnages perçus par l'utilisateur.
JanX2
2
Alors, quelle est la bonne réponse ici? L'appel advancesignifie que nous devons parcourir tout le chemin à travers une chaîne éventuellement longue. Mais former un NSRange et utiliser NSString explicitement est dangereux? Ce qui reste?
mat
1
Merci, m8. Cette transition vers Swift est un peu désordonnée après seulement 4 ans à faire Objective-C.
Felipe
162

Swift 2

Facile

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 Swift 4.

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"
Warif Akhand Rishi
la source
2
CE. Si déroutant que vous devez trouver votre index avant de pouvoir l'utiliser. Je ne sais pas pourquoi cela m'a pris autant de temps à réaliser, mais merci pour votre contribution au genre humain.
Warpzit
1
Dans swift 4, les opérations de sous-chaîne retournent une instance du type Substring, au lieu de String. Besoin de convertir le résultat en une chaîne pour un stockage à long terme. let newString = String (substring)
phnmnn
43

REMARQUE: @airspeedswift fait quelques 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 bêtes, et atteindre un index particulier peut prendre du temps O (n), ce qui signifie qu'une boucle qui utilise un indice peut être O (n ^ 2). Tu étais prévenu.

Vous avez juste besoin d'ajouter une nouvelle subscriptfonction qui prend une plage et utilise advancedBy()pour marcher 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. Dupe 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.)

Rob Napier
la source
2
Nous devrions probablement travailler autant que possible avec String.Index au lieu d'index entiers pour des raisons de performances. Chaque conversion d'un index entier en String.Index doit traiter la chaîne à rechercher.
JanX2
11
@AndrewDunn Ce n'est pas une optimisation prématurée. Chaque conversion d'un index entier en String.Index est O (n), pas O (1)! Veuillez lire les commentaires pour advance<T>(…)dans l' Swiftespace de noms.
JanX2
1
L'ensemble de l'implémentation de String se fait à l'aide de String.Index. Dans ce cas, il ne s'agit pas autant d'optimisation que de tout garder simple. Devoir jongler entre int et String.Index aller-retour partout mènera à un gâchis.
chakrit
@chakrit Uniquement si les index que vous devez utiliser ne sont pas déjà des entiers (au lieu d'être String.Indexes), ce qui est souvent le cas. Alors vous pouvez simplement souhaiter que toutes ces méthodes de manipulation de chaînes fonctionnent avec Ints. Et vous êtes obligé de le convertir, ce String.Indexesqui conduit au désordre que vous avez mentionné.
Rasto
Le lecteur intéressé doit jeter un œil à ExSwift github.com/pNre/ExSwift
AsTeR
42

Au moment où j'écris, aucune extension n'est parfaitement compatible avec Swift 4.2 , alors voici une qui couvre tous les besoins auxquels je pourrais 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)vous obtient: ello,Wo

string.substring(to: 7)vous obtient: Hello,Wo

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

string.substring(from: 1, length: 4)vous obtient: ello

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

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

hivernisé
la source
Comment cela gère-t-il les emoji et les grappes de graphèmes étendus? (Je devrais le tester moi-même mais ce n'est pas pratique pour le moment.)
Suragch
2
Il @Suragch il gère OK: "Hello😇😍😘😎📸📀💾∝ℵℶ≹".substring(from: 4, to: 14)nous donne:o😇😍😘😎📸📀💾∝ℵℶ
hivernisé
Ils ressemblent cependant à des personnages normaux. Essayez d'utiliser des emoji comme 👩‍👩‍👧‍👦 ou 🇨🇦🇺🇸🇲🇽
Supuhstar
31

SWIFT 2.0

Facile:

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 retournent une instance du type Substring, au lieu de String.

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)
phnmnn
la source
4
Malheureusement plus compatible avec Swift 3 à partir de Xcode 8 beta 6
Ben Morrow
1
Travaille pour moi. Xcode 8, Swift 3. Pas bêta sur Mac OS Sierra. text[text.startIndex..<text.index(text.startIndex, offsetBy: 2)]
Jose Ramirez
18

C'est beaucoup plus simple que n'importe laquelle des réponses ici, 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", le startIndex et le endIndex pourraient é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!

Veuillez regarder cette page

user1700737
la source
Oui, j'ai eu les mêmes problèmes avec le débogueur ne montrant pas la chaîne correcte plusieurs fois?
user1700737
j'ai une chaîne comme celle-ci / Date (147410000000) /. Comment puis-je obtenir de la valeur au lieu de spécifier l'index. J'ai besoin d'obtenir la valeur en utilisant rangeOfString ("(") et rangeOfString (")"). Le résultat devrait être 147410000000. Hoe puis-je obtenir cela
iPhone Guy
Ce n'est pas si difficile: var str = "/ Date (147410000000) /." var range = str.rangeOfString ("(") !. endIndex .. <str.rangeOfString (")") !. startIndex laissez myNewString = str.substringWithRange (range)
user1700737
4
depuis la advance(myString.endIndex, -1)syntaxe Xcode 7 beta 6 a changé enmyString.endIndex.advancedBy(-1)
l --marc l
16

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
}

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

Il est difficile de créer un String.Indexdirectement à partir d'une position entière utilisée dans le message d'ouverture. En effet, en mon nom, chaque caractère 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 (selon l'encodage utilisé). Alors, à quel octet doit se référer l'entier?

Il est possible de créer un nouveau à String.Indexpartir d'un existant en utilisant les méthodes succetpred qui s'assurera que le nombre correct d'octets est ignoré pour passer au point de code suivant dans l'encodage. Cependant, dans ce cas, il est plus facile de rechercher l'index du premier espace dans la chaîne pour trouver l'index de fin.

Joris Kluivers
la source
16

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. Chaîne est ponté à NSString, donc la plupart des méthodes NSString faire le travail directement sur une chaîne. Vous pouvez les utiliser librement et sans réfléchir. Ainsi, par exemple, cela fonctionne exactement comme vous vous y attendez:

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

Mais, comme cela arrive souvent, "J'ai fait travailler mon mojo, mais ça ne marche pas pour vous." Vous venez de choisir l'un des rares cas où une méthode Swift parallèle portant le même nom existe , et dans un cas comme celui-ci, la méthode Swift éclipse la méthode Objective-C. Ainsi, lorsque vous dites str.substringWithRange, Swift pense que vous voulez dire la méthode Swift plutôt que la méthode NSString - et puis vous êtes arrosé, car la méthode Swift attend unRange<String.Index> , et vous ne savez pas comment en faire un.

Le moyen le plus simple est d'empêcher Swift de faire de l'ombre comme ça, en lançant 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"; la chaîne est en fait une chaîne NSString. Nous disons simplement à Swift à quoi ressembler cette variable pour les besoins de cette seule ligne de code.

La partie vraiment bizarre de tout cela est que la méthode Swift, qui cause tous ces problèmes, n'est pas documentée. Je n'ai aucune idée où il 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.

mat
la source
Cependant, je suggère de déposer un bug. Apple devrait «pêcher ou couper l'appât» - soit nous donner un moyen d'en construire un Range<String.Index>, soit éliminer cette méthode non documentée.
mat
Merde - c'est utile. Malgré ce que dit la documentation, je n'ai pas compris ce qui n'allait pas jusqu'à ce que je lise ceci. Maintenant trié :)
Jon M
13

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:

entrez la description de l'image ici

Claudio Guirunas
la source
2
.advancedBy(0) n'est pas nécessaire pour startIndex.
Mat0
12

La réponse courte est que c'est vraiment difficile en Swift en ce moment. Mon intuition est qu'il y a encore beaucoup de travail à faire par Apple sur les méthodes pratiques pour des choses comme ça.

String.substringWithRange()attend un Range<String.Index>paramètre, et pour autant que je sache, il n'y a pas de méthode de générateur pour le String.Indextype. Vous pouvez récupérer des String.Indexvaleurs de aString.startIndexet aString.endIndexet appeler .succ()ou .pred()sur eux, mais c'est de la folie.

Que diriez-vous d'une extension sur la classe String qui prend 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 [dans la version bêta 3 et antérieure], même les chaînes natives de Swift ont des 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)
}

Production:

1
:
1
️
⃣
Nate Cook
la source
1
C'est étrange, les classes Swift de base semblent simplement manquer de nombreuses fonctions de base. Je ne peux pas penser qu'Apple veuille que nous référençons continuellement les classes Foundation pour effectuer des tâches simples.
bluedevil2k
1
Totalement. Gardez à l'esprit que Swift a été essentiellement développé dans l'obscurité, et tout le monde a une définition différente de "tâches simples" - je parie que nous voyons une itération rapide sur ces questions.
Nate Cook
@ JanX2 - Merci, vous avez tout à fait raison. Le pontage transparent avec NSStringm'a fait penser que j'utilisais uniquement des Stringméthodes natives de Swift , car je n'utilisais aucun myString as NSStringappel.
Nate Cook
Il existe également des bogues Unicode dans la gestion des chaînes natives de Swift: voir ma note à la fin.
Nate Cook
@NateCook Pouvez-vous s'il vous plaît déposer un radar pour ce problème?
JanX2
11

Vous pouvez utiliser ces extensions 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
ChikabuZ
la source
7

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

(i) Sous-chaîne 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)

Production:-

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)

Production:-

Swift is very powerful language!
is

J'espère que cela vous aidera!

Umang Kothari
la source
7

Solution facile avec peu de code.

Faites une extension qui inclut une sous-chaîne 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)
    }
}

Appelez simplement cela avec

someString.subString(start: 0, end: 6)
Oliver Dixon
la source
1
Le paramètre «end» signifie en fait la longueur ici, donc je pense qu'il devrait être renommé en cela.
Jovan Jovanovski
Je pense que l'utilisation de la longueur est assez déroutante. C'est en fait la «fin» de l'endroit où vous souhaitez que la sous-chaîne soit, et non la longueur réelle du résultat des sous-chaînes.
Oliver Dixon
6

Cela fonctionne dans mon terrain de jeu :)

String(seq: Array(str)[2...4])
patrykens
la source
En fait, c'est plus efficace que d'appeleradvance
Erik Aigner
2
Cela ne fonctionne pas dans mon aire de jeux Xcode 7 (Swift 2), mais cela fonctionne ... 'var str = "Bonjour, aire de jeux"' String (Array (str.characters) [7]) // "p" String (Array ( str.characters) [7 ... 10]) // "play" String (Array (str.characters) [7 .. <10]) // "pla"
Vince O'Sullivan
6

Mis à jour pour Xcode 7. Ajoute 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)
        }
    }

}
Firo
la source
4

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))

ça vous donnera "bonjour, p"

Cependant, lorsque cela devient vraiment intéressant, c'est que si vous rendez le dernier index plus grand que la chaîne dans l'aire de jeux, 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 doit connaître le type avec lequel il traite.

Vous devez également lui donner la chaîne réelle qui vous intéresse dans les aires de jeux car elle semble contenir toutes les piqûres l'une après l'autre 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 "bonjour, terrain de jeu str Je suis la chaîne suivante str2 "

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

:)

Peter Roberts
la source
Je suis d'accord qu'il est verbeux par rapport à Objective-C, mais tout tient sur une seule ligne, ce qui est un bonus.
Peter Roberts
Eh bien, il manque toujours une ligne qui imprime la sous-chaîne en fonction de la plage que vous avez calculée. J'aime certaines des autres solutions qui créent des extensions de classe String en utilisant votre logique, mais dans une extension. C'est finalement la solution que j'ai choisie.
Roderic Campbell
Ils ont corrigé le bug où vous pouvez aller au-delà de la fin de la chaîne. Maintenant, vous ne pouvez mettre qu'une valeur jusqu'à str.endIndex
Peter Roberts
4

Rob Napier avait déjà donné une réponse impressionnante en utilisant l'indice. Mais j'ai ressenti un inconvénient car il n'y a pas de vérification des conditions hors limite. Cela peut avoir tendance à planter. 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 laisser

if let string = str[0...5]
{
    //Manipulate your string safely
}
iPrabu
la source
Vous avez un bogue là-bas - endIndex devrait être startIndex avancé par endIndex seul, pas la distance entre les indices.
Aviad Ben Dov
3

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)
Sandu
la source
2

En prenant une page de Rob Napier, j'ai développé ces extensions de chaîne communes , 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"
Albert Bori
la source
Vous pouvez utiliser à la Rangeplace de Range<String.Index>grâce à l'inférence de type.
Dan Rosenstark
2

Voici comment vous obtenez une plage 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 passez une plage de valeurs de type String.Index (c'est ce que retourne l'avance) au lieu d'entiers.

La raison pour laquelle cela est nécessaire, c'est que les chaînes dans Swift n'ont pas d'accès aléatoire (en raison de la longueur variable des caractères Unicode essentiellement). Vous ne pouvez pas non plus le 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 la réponse de Rob Napier ).

Ixx
la source
2

J'ai essayé de trouver quelque chose de pythonique.

Tous les indices ici sont excellents, mais les fois où j'ai vraiment besoin de quelque chose de simple, c'est généralement quand je veux compter à l'envers, par exemple string.endIndex.advancedBy(-1)

Il prend en charge les nilvaleurs, pour l'index de début et de fin, où nilsignifierait un index à 0 pour le début, string.characters.countpour la fin.

extension String {
    var subString: (Int?) -> (Int?) -> String {
        return { (start) in
            { (end) in
                let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
                let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)

                return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
            }
        }
    }
}

let dog = "Dog‼🐶"
print(dog.subString(nil)(-1)) // Dog!!

ÉDITER

Une solution plus élégante:

public extension String {
    struct Substring {
        var start: Int?
        var string: String

        public subscript(end: Int?) -> String {
            let startIndex = start ?? 0 < 0 ? string.endIndex.advancedBy(start!) : string.startIndex.advancedBy(start ?? 0)
            let endIndex = end ?? string.characters.count < 0 ? string.endIndex.advancedBy(end!) : string.startIndex.advancedBy(end ?? string.characters.count)

            return startIndex > endIndex ? "" : string.substringWithRange(startIndex ..< endIndex)
        }
    }

    public subscript(start: Int?) -> Substring {
        return Substring(start: start, string: self)
    }
}

let dog = "Dog‼🐶"
print(dog[nil][-1]) // Dog!!
funct7
la source
1

Créez d'abord la plage, puis la sous-chaîne. Vous pouvez utiliser la fromIndex..<toIndexsyntaxe comme ceci:

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

voici un exemple pour obtenir l'ID vidéo uniquement .ie (6oL687G0Iso) à partir de l'URL entière 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)
Hardik Thakkar
la source
1
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

Échantillon complet que vous pouvez voir ici

Svitlana
la source
0

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 conjonction avec NSMakeRange prend la partie de la chaîne commençant à l'index 6 (caractère "W") et se terminant à l'index 6 + 6 positions en avant (caractère "!")

À votre santé.

sittisal
la source
Je déteste ça, mais c'est la seule chose qui semble fonctionner pour moi dans la Bêta 4.
Roderic Campbell
0

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"
William Falcon
la source
0

Si vous en avez un NSRange, le pontage NSStringfonctionne parfaitement. Par exemple, je travaillais avec UITextFieldDelegateet je voulais rapidement calculer la nouvelle valeur de chaîne lorsqu'il m'a demandé s'il 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)
}
joslinm
la source
0

Extension simple pour String:

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}
Bartłomiej Semańczyk
la source
0

Si vous ne vous souciez pas des performances ... 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"
Jiachen Ren
la source