¿Cómo funciona la subcadena String en Swift?

Resuelto Suragch asked hace 8 años • 24 respuestas

He estado actualizando algunos de mis códigos y respuestas antiguos con Swift 3, pero cuando llegué a Swift Strings e Indexación con subcadenas, las cosas se volvieron confusas.

Específicamente estaba intentando lo siguiente:

let str = "Hello, playground"
let prefixRange = str.startIndex..<str.startIndex.advancedBy(5)
let prefix = str.substringWithRange(prefixRange)

donde la segunda línea me daba el siguiente error

El valor de tipo 'String' no tiene ningún miembro 'substringWithRange'

Veo que Stringahora tiene los siguientes métodos:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

Al principio esto me confundió mucho, así que comencé a jugar con el índice y el rango . Esta es una pregunta y respuesta de seguimiento para la subcadena. Estoy agregando una respuesta a continuación para mostrar cómo se usan.

Suragch avatar Sep 24 '16 21:09 Suragch
Aceptado

ingrese la descripción de la imagen aquí

Todos los siguientes ejemplos utilizan

var str = "Hello, playground"

veloz 4

Las cadenas recibieron una revisión bastante grande en Swift 4. Cuando ahora obtienes alguna subcadena de una cadena, obtienes un Substringtipo en lugar de un archivo String. ¿Por qué es esto? Las cadenas son tipos de valores en Swift. Eso significa que si usa una cadena para crear una nueva, debe copiarla. Esto es bueno para la estabilidad (nadie más va a cambiarlo sin tu conocimiento) pero malo para la eficiencia.

Una subcadena, por otro lado, es una referencia a la cadena original de la que proviene. Aquí hay una imagen de la documentación que lo ilustra.

No es necesario realizar copias, por lo que su uso es mucho más eficiente. Sin embargo, imagina que obtienes una subcadena de diez caracteres de una cadena de un millón de caracteres. Debido a que la Subcadena hace referencia a la Cadena, el sistema tendría que retener la Cadena completa mientras la Subcadena esté disponible. Por lo tanto, cuando haya terminado de manipular su Subcadena, conviértala en una Cadena.

let myString = String(mySubstring)

Esto copiará solo la subcadena y se podrá recuperar la memoria que contiene la cadena antigua . Las subcadenas (como tipo) están destinadas a ser de corta duración.

Otra gran mejora en Swift 4 es que las cadenas son colecciones (nuevamente). Eso significa que todo lo que pueda hacer con una Colección, puede hacerlo con una Cadena (usar subíndices, iterar sobre los caracteres, filtrar, etc.).

Los siguientes ejemplos muestran cómo obtener una subcadena en Swift.

Obteniendo subcadenas

Puede obtener una subcadena a partir de una cadena mediante el uso de subíndices o de otros métodos (por ejemplo, prefix, suffix, split). Sin embargo , aún necesita usar String.Indexy no un Intíndice para el rango. (Vea mi otra respuesta si necesita ayuda con eso).

Principio de una cuerda

Puede utilizar un subíndice (tenga en cuenta el rango unilateral de Swift 4):

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str[..<index] // Hello

o prefix:

let index = str.index(str.startIndex, offsetBy: 5)
let mySubstring = str.prefix(upTo: index) // Hello

o incluso más fácil:

let mySubstring = str.prefix(5) // Hello

Fin de una cuerda

Usando subíndices:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str[index...] // playground

o suffix:

let index = str.index(str.endIndex, offsetBy: -10)
let mySubstring = str.suffix(from: index) // playground

o incluso más fácil:

let mySubstring = str.suffix(10) // playground

Tenga en cuenta que cuando usé suffix(from: index)tuve que contar hacia atrás desde el final usando -10. Eso no es necesario cuando solo se usa suffix(x), que solo toma los últimos xcaracteres de una cadena.

Rango en una cadena

Nuevamente aquí simplemente usamos subíndices.

let start = str.index(str.startIndex, offsetBy: 7)
let end = str.index(str.endIndex, offsetBy: -6)
let range = start..<end

let mySubstring = str[range]  // play

Convirtiendo SubstringaString

No olvide que cuando esté listo para guardar su subcadena, debe convertirla en a Stringpara que se pueda limpiar la memoria de la cadena anterior.

let myString = String(mySubstring)

¿ Usando una Intextensión de índice?

Dudo en usar una Intextensión de índice basada después de leer el artículo Strings in Swift 3 de Airspeed Velocity y Ole Begemann. Aunque en Swift 4 las cadenas son colecciones, el equipo de Swift no ha utilizado Intíndices a propósito. Aún es String.Index. Esto tiene que ver con que los caracteres Swift estén compuestos por un número variable de puntos de código Unicode. El índice real debe calcularse de forma única para cada cadena.

Debo decir que espero que el equipo de Swift encuentre una manera de abstraerse String.Indexen el futuro. Pero hasta entonces, elijo utilizar su API. Me ayuda recordar que las manipulaciones de cadenas no son simples Intbúsquedas de índices.

Suragch avatar Sep 24 '2016 14:09 Suragch

Estoy realmente frustrado con el modelo de acceso a cadenas de Swift: todo tiene que ser un archivo Index. Todo lo que quiero es acceder al carácter i-ésimo de la cadena usando Int, no el índice torpe y avanzar (que cambia con cada versión importante). Entonces hice una extensión a String:

extension String {
    func index(from: Int) -> Index {
        return self.index(startIndex, offsetBy: from)
    }

    func substring(from: Int) -> String {
        let fromIndex = index(from: from)
        return String(self[fromIndex...])
    }

    func substring(to: Int) -> String {
        let toIndex = index(from: to)
        return String(self[..<toIndex])
    }

    func substring(with r: Range<Int>) -> String {
        let startIndex = index(from: r.lowerBound)
        let endIndex = index(from: r.upperBound)
        return String(self[startIndex..<endIndex])
    }
}

let str = "Hello, playground"
print(str.substring(from: 7))         // playground
print(str.substring(to: 5))           // Hello
print(str.substring(with: 7..<11))    // play
Code Different avatar Sep 24 '2016 14:09 Code Different

Extensión Swift 5:

extension String {
    subscript(_ range: CountableRange<Int>) -> String {
        let start = index(startIndex, offsetBy: max(0, range.lowerBound))
        let end = index(start, offsetBy: min(self.count - range.lowerBound, 
                                             range.upperBound - range.lowerBound))
        return String(self[start..<end])
    }

    subscript(_ range: CountablePartialRangeFrom<Int>) -> String {
        let start = index(startIndex, offsetBy: max(0, range.lowerBound))
         return String(self[start...])
    }
}

Uso:

let s = "hello"
s[0..<3] // "hel"
s[3...]  // "lo"

O unicode:

let s = "😎🤣😋"
s[0..<1] // "😎"
Lou Zell avatar Sep 09 '2017 17:09 Lou Zell