Obtener el enésimo carácter de una cadena en Swift

Resuelto Mohsen asked hace 10 años • 47 respuestas

¿Cómo puedo obtener el enésimo carácter de una cadena? Probé []el descriptor de acceso bracket() sin suerte.

var string = "Hello, world!"

var firstChar = string[0] // Throws error

ERROR: 'subíndice' no está disponible: no se puede subíndice una cadena con un Int, consulte el comentario de la documentación para obtener más información

Mohsen avatar Jun 07 '14 08:06 Mohsen
Aceptado

Atención: consulte la respuesta de Leo Dabus para conocer una implementación adecuada para Swift 4 y Swift 5.

Swift 4 o posterior

El Substringtipo se introdujo en Swift 4 para hacer que las subcadenas sean más rápidas y eficientes al compartir almacenamiento con la cadena original, por lo que eso es lo que deberían devolver las funciones de subíndice.

Pruébalo aquí

extension StringProtocol {
    subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
    subscript(range: Range<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}

Para convertir Substringen a String, simplemente puede hacerlo String(string[0..2]), pero solo debe hacerlo si planea mantener la subcadena. De lo contrario, es más eficiente mantenerlo como Substring.

Sería fantástico si alguien pudiera encontrar una buena manera de fusionar estas dos extensiones en una. Intenté ampliar StringProtocol sin éxito porque el indexmétodo no existe allí. Nota: Esta respuesta ya ha sido editada, está implementada correctamente y ahora también funciona para subcadenas. Solo asegúrese de utilizar un rango válido para evitar fallas al subscribir su tipo StringProtocol. Para subíndices con un rango que no fallará con valores fuera de rango, puede usar esta implementación


¿Por qué no está integrado?

El mensaje de error dice "consulte el comentario de la documentación para su discusión" . Apple proporciona la siguiente explicación en el archivo UnavailableStringAPIs.swift :

La subscripción de cadenas con números enteros no está disponible.

El concepto de "el icarácter enésimo de una cadena" tiene diferentes interpretaciones en diferentes bibliotecas y componentes del sistema. Se debe seleccionar la interpretación correcta según el caso de uso y las API involucradas, por lo que String no se puede subscribir con un número entero.

Swift proporciona varias formas diferentes de acceder a los datos de caracteres almacenados dentro de cadenas.

  • String.utf8es una colección de unidades de código UTF-8 en la cadena. Utilice esta API al convertir la cadena a UTF-8. La mayoría de las API POSIX procesan cadenas en términos de unidades de código UTF-8.

  • String.utf16es una colección de unidades de código UTF-16 en cadena. La mayoría de las API Cocoa y Cocoa Touch procesan cadenas en términos de unidades de código UTF-16. Por ejemplo, instancias de desplazamientos y longitudes de subcadenas NSRangeutilizadas NSAttributedStringy NSRegularExpressionalmacenadas en términos de unidades de código UTF-16.

  • String.unicodeScalarses una colección de escalares Unicode. Utilice esta API cuando realice manipulación de bajo nivel de datos de personajes.

  • String.characterses una colección de grupos de grafemas extendidos, que son una aproximación de los caracteres percibidos por el usuario.

Tenga en cuenta que al procesar cadenas que contienen texto legible por humanos, se debe evitar en la mayor medida posible el procesamiento carácter por carácter. Utilice en su lugar algoritmos Unicode sensibles a la configuración regional de alto nivel, por ejemplo, String.localizedStandardCompare(), etc.String.localizedLowercaseStringString.localizedStandardRangeOfString()

aleclarson avatar Jun 10 '2014 15:06 aleclarson

Rápido 5.2

let str = "abcdef"
str[1 ..< 3] // returns "bc"
str[5] // returns "f"
str[80] // returns ""
str.substring(fromIndex: 3) // returns "def"
str.substring(toIndex: str.length - 2) // returns "abcd"

Deberá agregar esta extensión String a su proyecto (está completamente probada):

extension String {

    var length: Int {
        return count
    }

    subscript (i: Int) -> String {
        return self[i ..< i + 1]
    }

    func substring(fromIndex: Int) -> String {
        return self[min(fromIndex, length) ..< length]
    }

    func substring(toIndex: Int) -> String {
        return self[0 ..< max(0, toIndex)]
    }

    subscript (r: Range<Int>) -> String {
        let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                            upper: min(length, max(0, r.upperBound))))
        let start = index(startIndex, offsetBy: range.lowerBound)
        let end = index(start, offsetBy: range.upperBound - range.lowerBound)
        return String(self[start ..< end])
    }
}

Aunque Swift siempre tuvo una solución lista para usar para este problema (sin la extensión String, que proporcioné a continuación), recomiendo encarecidamente usar la extensión. ¿Por qué? Porque me ahorró decenas de horas de dolorosa migración desde las primeras versiones de Swift, donde la sintaxis de String cambiaba en casi cada versión, pero todo lo que necesitaba hacer era actualizar la implementación de la extensión en lugar de refactorizar todo el proyecto. Haz tu elección.

let str = "Hello, world!"
let index = str.index(str.startIndex, offsetBy: 4)
str[index] // returns Character 'o'

let endIndex = str.index(str.endIndex, offsetBy:-2)
str[index ..< endIndex] // returns String "o, worl"

String(str.suffix(from: index)) // returns String "o, world!"
String(str.prefix(upTo: index)) // returns String "Hell"
nalexn avatar Nov 06 '2014 09:11 nalexn

Se me acaba de ocurrir esta ingeniosa solución

var firstChar = Array(string)[0]
Jens Wirth avatar Jun 12 '2014 06:06 Jens Wirth