¿Cómo se usa String.substringWithRange? (o, ¿cómo funcionan los rangos en Swift?)

266

Todavía no he podido descubrir cómo obtener una subcadena de a Stringen Swift:

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

No puedo crear un Rango en Swift. Autocompletar en el área de juegos no es muy útil: esto es lo que sugiere:

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

No he encontrado nada en la Biblioteca de referencia estándar de Swift que me ayude. Aquí había otra conjetura salvaje:

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

Y esto:

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

He visto otras respuestas ( Búsqueda de índice de caracteres en Swift String ) que parecen sugerir que, dado que Stringes un tipo de puente NSString, los métodos "antiguos" deberían funcionar, pero no está claro cómo, por ejemplo, esto tampoco funciona ( no parece ser una sintaxis válida):

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

Pensamientos?

Robar
fuente
¿Qué quieres decir cuando dices que el último ejemplo no funciona? ¿Causa un error o devuelve un valor inesperado?
67cherries
Dupe rdar: // 17158813 solicitando notación de subíndice openradar.appspot.com/radar?id=6373877630369792
Rob Napier

Respuestas:

259

Puede usar el método substringWithRange. Se necesita un inicio y fin de String.Index.

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

Para cambiar el índice inicial y final, use advancedBy (n).

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

También puede seguir utilizando el método NSString con NSRange, pero debe asegurarse de utilizar un NSString como este:

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

Nota: como se menciona en JanX2, este segundo método no es seguro con cadenas unicode.

Connor
fuente
55
Si. Por ahora seguiría uniendo a NSString cuando fuera necesario. Parece que la cadena de Swift aún está incompleta
Connor
14
¡Esto no es seguro! Está suponiendo que los índices en String y NSString son intercambiables. Ese no es el caso. ¡Los índices en una cadena se refieren a puntos de código Unicode mientras que los índices en una cadena NSS se refieren a unidades de código UTF-16! Pruébalo con emoji.
JanX2
3
Tenga en cuenta que los índices en una cadena ya no se refieren a puntos de código Unicode. Piense en ellos como referenciando personajes percibidos por el usuario.
JanX2
2
Entonces, ¿cuál es la respuesta correcta aquí? Llamar advancesignifica que tenemos que recorrer todo el camino a través de una cadena posiblemente larga. ¿Pero formar un NSRange y usar NSString explícitamente es peligroso? ¿Lo que queda?
mate
1
Gracias m8. Esta transición a Swift es algo desordenada después de 4 años solo haciendo Objective-C.
Felipe
162

Swift 2

Sencillo

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:)y substring(from:)están en desuso en Swift 4.

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"
Warif Akhand Rishi
fuente
2
ESTA. Tan confuso que tiene que encontrar su índice antes de poder usarlo. No tengo idea de por qué me tomó tanto tiempo darme cuenta, pero gracias por tu contribución a la especie humana.
Warpzit
1
En las operaciones rápidas de 4 subcadenas, se devuelve una instancia del tipo Substring, en lugar de String. Necesita convertir el resultado en una cadena para el almacenamiento a largo plazo. let newString = String (substring)
phnmnn
43

NOTA: @airspeedswift hace algunos puntos muy perspicaces sobre las compensaciones de este enfoque, particularmente los impactos ocultos en el rendimiento. Las cadenas no son simples bestias, y llegar a un índice particular puede llevar tiempo O (n), lo que significa que un bucle que usa un subíndice puede ser O (n ^ 2). Usted ha sido advertido.

Solo necesita agregar una nueva subscriptfunción que tome un rango y use advancedBy()para caminar hacia donde desee:

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"

(Esto definitivamente debería ser parte del lenguaje. Por favor engañe rdar: // 17158813 )

Por diversión, también puede agregar un +operador a los índices:

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)

(Todavía no sé si este debería ser parte del lenguaje o no).

Rob Napier
fuente
2
Probablemente deberíamos trabajar con String.Index en lugar de índices enteros tanto como sea posible por razones de rendimiento. Cada conversión de un índice entero en un String.Index necesita procesar la cadena para buscar.
JanX2
11
@ AndrewDunn Esto no es una optimización prematura. ¡Cada conversión de un índice entero en un String.Index es O (n), no O (1)! Por favor, lea los comentarios advance<T>(…)en el Swiftespacio de nombres.
JanX2
1
Toda la implementación de String se realiza mediante String.Index. En este caso, no es tanta optimización como mantener todo directo. Tener que hacer malabarismos entre int y String. El índice de ida y vuelta en todas partes conducirá a un desastre.
chakrit
@chakrit Solo si los índices que necesita usar ya no son enteros (en lugar de ser String.Indexes), lo que suele ser el caso. De lo que puede desear, todos esos métodos de manejo de cadenas estaban trabajando con Ints. Y se ve obligado a convertirlo, lo String.Indexesque conduce al desastre que mencionó.
Rasto
El lector interesado debe echar un vistazo a ExSwift github.com/pNre/ExSwift
AsTeR
42

En el momento en que escribo, ninguna extensión es perfectamente compatible con Swift 4.2 , así que aquí hay una que cubre todas las necesidades que se me ocurren:

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

Y luego, puedes usar:

let string = "Hello,World!"

string.substring(from: 1, to: 7)te consigue: ello,Wo

string.substring(to: 7)te consigue: Hello,Wo

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

string.substring(from: 1, length: 4)te consigue: ello

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

Actualizado substring(from: Int?, length: Int)para admitir a partir de cero.

preparado para el invierno
fuente
¿Cómo maneja esto los emoji y los grupos de grafemas extendidos? (Debería probarlo yo mismo, pero no es conveniente en este momento.)
Suragch
2
@Suragch Lo maneja bien: "Hello😇😍😘😎📸📀💾∝ℵℶ≹".substring(from: 4, to: 14)nos da:o😇😍😘😎📸📀💾∝ℵℶ
Winterized
Sin embargo, esos parecen personajes normales. Intenta usar emoji como 👩‍👩‍👧‍👦 o 🇨🇦🇺🇸🇲🇽
Supuhstar
31

SWIFT 2.0

sencillo:

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

Las operaciones de subcadena devuelven una instancia del tipo Substring, en lugar 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
fuente
44
Ya no es compatible con Swift 3 a partir de Xcode 8 beta 6, lamentablemente
Ben Morrow
1
Funciona para mi. Xcode 8, Swift 3. No es beta en Mac OS Sierra. text[text.startIndex..<text.index(text.startIndex, offsetBy: 2)]
Jose Ramirez
18

Es mucho más simple que cualquiera de las respuestas aquí, una vez que encuentre la sintaxis correcta.

Quiero quitarme el [y]

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 )

el resultado será "ABCDEFGHI", el startIndex y endIndex también podrían usarse

let mySubString = myString.substringFromIndex(startIndex)

¡y así!

PD: Como se indica en los comentarios, ¡hay algunos cambios de sintaxis en swift 2 que viene con xcode 7 y iOS9!

Por favor mira esta página

usuario1700737
fuente
Sí, tuve los mismos problemas con el depurador que no muestra la cadena correcta varias veces.
user1700737
Tengo una cadena como esta / Fecha (147410000000) /. ¿Cómo puedo obtener valor en lugar de especificar el índice? Necesito obtener el valor usando rangeOfString ("(") y rangeOfString (")"). El resultado debería ser 147410000000. ¿Cómo puedo obtener eso?
iPhone Guy
Eso no es tan difícil: var str = "/ Date (147410000000) /." var range = str.rangeOfString ("(") !. endIndex .. <str.rangeOfString (")") !. startIndex deja que myNewString = str.substringWithRange (range)
usuario1700737
44
desde que la advance(myString.endIndex, -1)sintaxis de Xcode 7 beta 6 ha cambiado amyString.endIndex.advancedBy(-1)
l --marc l
16

Por ejemplo, para encontrar el primer nombre (hasta el primer espacio) en mi nombre completo:

let name = "Joris Kluivers"

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

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

starty endson de tipo String.Indexaquí y se usan para crear ay se Range<String.Index>usan en el descriptor de acceso de subíndice (si se encuentra un espacio en la cadena original).

Es difícil crear un String.Indexdirectamente desde una posición entera como se usa en la publicación de apertura. Esto se debe a que, en mi nombre, cada carácter tendría el mismo tamaño en bytes. Pero los caracteres que usan acentos especiales en otros idiomas podrían haber usado varios bytes más (dependiendo de la codificación utilizada). Entonces, ¿a qué byte debería referirse el entero?

Es posible crear uno nuevo a String.Indexpartir de uno existente usando los métodos succy predque asegurará que se omita el número correcto de bytes para llegar al siguiente punto de código en la codificación. Sin embargo, en este caso, es más fácil buscar el índice del primer espacio en la cadena para encontrar el índice final.

Joris Kluivers
fuente
16

Como String es un tipo de puente para NSString, los métodos "antiguos" deberían funcionar, pero no está claro cómo, por ejemplo, esto tampoco funciona (no parece ser una sintaxis válida):

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

Para mí, esa es la parte realmente interesante de tu pregunta. Cadena está puenteado a NSString, por lo que la mayor parte métodos NSString hacen el trabajo directamente en una cadena. Puedes usarlos libremente y sin pensar. Entonces, por ejemplo, esto funciona tal como espera:

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

Pero, como suele suceder, "conseguí que mi mojo funcionara pero simplemente no funciona en ti". Simplemente elegiste uno de los raros casos en los que existe un método Swift paralelo idénticamente llamado , y en un caso como ese, el método Swift eclipsa el método Objective-C. Por lo tanto, cuando dices str.substringWithRange, Swift cree que te refieres al método Swift en lugar del método NSString, y luego te mandan, porque el método Swift espera un Range<String.Index>, y no sabes cómo hacer uno de esos.

La salida fácil es evitar que Swift se eclipse de esta manera, emitiendo explícitamente:

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

Tenga en cuenta que aquí no hay trabajo adicional significativo. "Emitir" no significa "convertir"; la Cadena es efectivamente una NSString. Solo le estamos diciendo a Swift cómo mirar esta variable para los propósitos de esta línea de código.

La parte realmente extraña de todo esto es que el método Swift, que causa todos estos problemas, no está documentado. No tengo idea de dónde se define; no está en el encabezado NSString y tampoco está en el encabezado Swift.

mate
fuente
Sin embargo, sugiero presentar un error. Apple debería "pescar o cortar el cebo", ya sea para darnos una forma de construir un Range<String.Index>u obtener este método indocumentado de nuestro camino.
mate
Maldición, esto es útil. A pesar de lo que dice la documentación, no entendí lo que iba mal hasta que leí esto. Ahora ordenado :)
Jon M
13

En el nuevo uso 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:

ingrese la descripción de la imagen aquí

Claudio Guirunas
fuente
2
.advancedBy(0) no es necesario para startIndex.
Mat0
12

La respuesta corta es que esto es realmente difícil en Swift en este momento. Mi presentimiento es que todavía hay mucho trabajo por hacer para Apple en métodos convenientes para cosas como esta.

String.substringWithRange()está esperando un Range<String.Index>parámetro, y por lo que puedo decir, no hay un método generador para el String.Indextipo. Usted puede obtener String.Indexlos valores de vuelta de aString.startIndexy aString.endIndexy la llamada .succ()o .pred()sobre ellos, pero eso es una locura.

¿Qué tal una extensión en la clase String que toma buenos Ints viejos ?

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

Actualización: Swift beta 4 resuelve los siguientes problemas.

Tal como está [en beta 3 y anteriores], incluso las cadenas nativas de Swift tienen algunos problemas con el manejo de caracteres Unicode. El icono de perro anterior funcionó, pero lo siguiente no:

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

Salida:

1
:
1
️
⃣
Nate Cook
fuente
1
Es extraño, las clases básicas de Swift parecen carecer de muchas funciones básicas. No puedo pensar que Apple quiera que hagamos referencia continua a las clases de Foundation para realizar tareas simples.
bluedevil2k
1
Totalmente. Tenga en cuenta que Swift se desarrolló básicamente en la oscuridad, y todos tienen una definición diferente de "tareas simples". Apuesto a que vemos una iteración rápida sobre estos temas.
Nate Cook
@ JanX2 - Gracias, tienes toda la razón. El puente continuo NSStringme hizo pensar que estaba usando solo Stringmétodos nativos de Swift , ya que no estaba usando ninguna myString as NSStringllamada.
Nate Cook
En este momento también hay algunos errores Unicode en el manejo de cadenas nativas de Swift: vea mi nota al final.
Nate Cook
@NateCook ¿Puede archivar un radar para ese problema?
JanX2
11

Puedes usar estas extensiones para mejorar 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)
    }
}

Uso:

let str = "Hello, playground"

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

Código de muestra para obtener subcadenas en Swift 2.0

(i) Subcadena del índice inicial

Entrada:-

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

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

Salida:-

Swift is very powerful language!
Swift

(ii) Subcadena de un índice particular

Entrada:-

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

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

Salida:-

Swift is very powerful language!
is

¡Espero que te ayude!

Umang Kothari
fuente
7

Solución fácil con poco código.

Cree una extensión que incluya subcadenas básicas que tienen casi todos los demás idiomas:

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

Simplemente llame a esto con

someString.subString(start: 0, end: 6)
Oliver Dixon
fuente
1
El parámetro 'fin' en realidad significa longitud aquí, así que creo que debería cambiarse el nombre a eso.
Jovan Jovanovski
Creo que usar longitud es bastante confuso. De hecho, es el "final" de donde le gustaría que esté la subString, no la longitud real del resultado de la subString.
Oliver Dixon
6

Esto funciona en mi patio de recreo :)

String(seq: Array(str)[2...4])
patrykens
fuente
En realidad, esto es más eficiente que llamaradvance
Erik Aigner
2
No funciona en mi área de juegos Xcode 7 (Swift 2), pero sí ... 'var str = "Hola, área de juegos"' String (Array (caracteres str.) [7]) // "p" String (Array ( str.characters) [7 ... 10]) // "play" String (Array (str.characters) [7 .. <10]) // "pla"
Vince O'Sullivan
6

Actualizado para Xcode 7. Agrega la extensión de cadena:

Utilizar:

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

Implementación:

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
fuente
4

prueba esto en el patio de recreo

var str:String = "Hello, playground"

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

te dará "ello, p"

Sin embargo, cuando esto se vuelve realmente interesante es que si hace que el último índice sea más grande que la cadena en el patio de recreo, mostrará las cadenas que definió después de str: o

Range () parece ser una función genérica, por lo que necesita saber el tipo con el que está tratando.

También debe darle la cadena real que le interesa en los parques infantiles, ya que parece contener todas las picaduras en una secuencia una tras otra con su nombre de variable después.

Entonces

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

da "ello, playground str Im the next string str2 "

funciona incluso si str2 se define con let

:)

Peter Roberts
fuente
Estoy de acuerdo en que es detallado en comparación con Objective-C, sin embargo, todo encaja en una línea, lo cual es una ventaja.
Peter Roberts
Bueno, todavía falta una línea que realmente imprima la subcadena dado el rango que calculó. Me gustan algunas de las otras soluciones que crean extensiones de clase String usando su lógica, pero en una extensión. Esa es en última instancia la solución con la que fui.
Roderic Campbell
Han solucionado el error donde puedes ir más allá del final de la cadena. Ahora solo puede poner un valor hasta str.endIndex
Peter Roberts
4

Rob Napier ya había dado una respuesta asombrosa usando el subíndice. Pero sentí un inconveniente en eso, ya que no hay verificación para condiciones fuera de límite. Esto puede tender a fallar. Así que modifiqué la extensión y aquí está

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

Salida a continuación

var str2 = "Hello, World"

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

Así que sugiero que siempre verifique si se deja

if let string = str[0...5]
{
    //Manipulate your string safely
}
iPrabu
fuente
Tiene un error allí: endIndex debe ser startIndex avanzado solo por endIndex, no la distancia entre los índices.
Aviad Ben Dov
3

En Swift3

Por ejemplo: una variable "Duke James Thomas", necesitamos obtener "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
fuente
2

Tomando una página de Rob Napier, desarrollé estas extensiones de cadena comunes , dos de las cuales son:

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

Uso:

"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"
Albert Bori
fuente
Puede usar en Rangelugar de Range<String.Index>gracias a la inferencia de tipos.
Dan Rosenstark
2

Así es como obtienes un rango de una cadena:

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"

El punto clave es que está pasando un rango de valores de tipo String.Index (esto es lo que devuelve el avance) en lugar de enteros.

La razón por la que esto es necesario es que las cadenas en Swift no tienen acceso aleatorio (debido a la longitud variable de los caracteres Unicode básicamente). Tampoco puedes hacer str[1]. String.Index está diseñado para funcionar con su estructura interna.

Sin embargo, puede crear una extensión con un subíndice, que lo hace por usted, por lo que puede pasar un rango de enteros (ver, por ejemplo, la respuesta de Rob Napier ).

Ixx
fuente
2

Traté de llegar a algo Pythonic.

Todos los subíndices aquí son geniales, pero las veces que realmente necesito algo simple es cuando quiero contar desde atrás, p. Ej. string.endIndex.advancedBy(-1)

Admite nilvalores, tanto para el índice inicial como para el final, donde nilsignificaría un índice en 0 para el inicio, string.characters.countpara el final.

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

EDITAR

Una solución más elegante:

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
fuente
1

Primero cree el rango, luego la subcadena. Puede usar la fromIndex..<toIndexsintaxis de esta manera:

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

Aquí hay un ejemplo para obtener solo la identificación de video .ie (6oL687G0Iso) de toda la URL en forma rápida

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
fuente
1
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

Muestra completa que puedes ver aquí

Svitlana
fuente
0

Bueno, tuve el mismo problema y lo resolví con la función "bridgeToObjectiveC ()":

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

Tenga en cuenta que en el ejemplo, substringWithRange junto con NSMakeRange toman la parte de la cadena que comienza en el índice 6 (carácter "W") y termina en el índice 6 + 6 posiciones adelante (carácter "!")

Salud.

sittisal
fuente
Odio esto, pero es lo único que parece estar funcionando para mí en Beta 4.
Roderic Campbell
0

Puede usar cualquiera de los métodos de subcadena en una extensión Swift String que escribí https://bit.ly/JString .

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

Si tiene un NSRangepuente, NSStringfunciona a la perfección. Por ejemplo, estaba haciendo algo de trabajo UITextFieldDelegatey rápidamente quise calcular el nuevo valor de la cadena cuando me preguntó si debería reemplazar el rango.

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
fuente
0

Extensión simple para String:

extension String {

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

Si no le importa el rendimiento ... esta es probablemente la solución más concisa en 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)}
    }
}

Te permite hacer algo como esto:

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