ios - Swift: divide una cadena en una matriz


Digamos que tengo una cadena aquí:

var fullName: String = "First Last"

Quiero dividir la base de cadena en espacios en blanco y asignar los valores a sus respectivas variables

var fullNameArr = // something like: fullName.explode(" ") 

var firstName: String = fullNameArr[0]
var lastName: String? = fullnameArr[1]

Además, a veces los usuarios pueden no tener un apellido.



Answers


La forma de Swift es usar la función de split global, así:

var fullName = "First Last"
var fullNameArr = split(fullName) {$0 == " "}
var firstName: String = fullNameArr[0]
var lastName: String? = fullNameArr.count > 1 ? fullNameArr[1] : nil

con Swift 2

En Swift 2, el uso de split se vuelve un poco más complicado debido a la introducción del tipo de CharacterView interno. Esto significa que String ya no adopta los protocolos SequenceType o CollectionType y en su lugar debe usar la propiedad .characters para acceder a una representación de tipo CharacterView de una instancia de String. (Nota: CharacterView adopta los protocolos SequenceType y CollectionType).

let fullName = "First Last"
let fullNameArr = fullName.characters.split{$0 == " "}.map(String.init)
// or simply:
// let fullNameArr = fullName.characters.split{" "}.map(String.init)

fullNameArr[0] // First
fullNameArr[1] // Last 



Simplemente llame al método componentsSeparatedByString en su fullName

import Foundation

var fullName: String = "First Last"
let fullNameArr = fullName.componentsSeparatedByString(" ")

var firstName: String = fullNameArr[0]
var lastName: String = fullNameArr[1]

Actualización para Swift 3+

import Foundation

let fullName    = "First Last"
let fullNameArr = fullName.components(separatedBy: " ")

let name    = fullNameArr[0]
let surname = fullNameArr[1]



El método más fácil de hacerlo es mediante el uso de componentsSeparatedBy:

Para Swift 2:

import Foundation
let fullName : String = "First Last";
let fullNameArr : [String] = fullName.componentsSeparatedByString(" ")

// And then to access the individual words:

var firstName : String = fullNameArr[0]
var lastName : String = fullNameArr[1]

Para Swift 3:

import Foundation

let fullName : String = "First Last"
let fullNameArr : [String] = fullName.components(separatedBy: " ")

// And then to access the individual words:

var firstName : String = fullNameArr[0]
var lastName : String = fullNameArr[1]



Swift Dev. 4.0 (24 de mayo de 2017)

Una nueva función split en Swift 4 ( Beta ).

import Foundation
let sayHello = "Hello Swift 4 2017";
let result = sayHello.split(separator: " ")
print(result)

Salida:

["Hello", "Swift", "4", "2017"]

Acceso a valores:

print(result[0]) // Hello
print(result[1]) // Swift
print(result[2]) // 4
print(result[3]) // 2017

Xcode 8.1 / Swift 3.0.1

Aquí está la forma de delimitadores múltiples con matriz.

import Foundation
let mathString: String = "12-37*2/5"
let numbers = mathString.components(separatedBy: ["-", "*", "/"])
print(numbers)

Salida:

["12", "37", "2", "5"]



Como alternativa a la respuesta de WMios, también puede usar componentsSeparatedByCharactersInSet , que puede ser útil en caso de que tenga más separadores (espacio en blanco, coma, etc.).

Con su entrada específica:

let separators = NSCharacterSet(charactersInString: " ")
var fullName: String = "First Last";
var words = fullName.componentsSeparatedByCharactersInSet(separators)

// words contains ["First", "Last"]

Usando separadores múltiples:

let separators = NSCharacterSet(charactersInString: " ,")
var fullName: String = "Last, First Middle";
var words = fullName.componentsSeparatedByCharactersInSet(separators)

// words contains ["Last", "First", "Middle"]



Xcode 9 Swift 4 o Xcode 8.2.1 • Swift 3.0.2

Si solo necesita formatear correctamente el nombre de una persona, puede usar PersonNameComponentsFormatter .

La clase PersonNameComponentsFormatter proporciona representaciones localizadas de los componentes del nombre de una persona, representados por un objeto PersonNameComponents. Utilice esta clase para crear nombres localizados al mostrar la información del nombre de la persona al usuario.

// iOS (9.0 and later), macOS (10.11 and later), tvOS (9.0 and later), watchOS (2.0 and later)
let nameFormatter = PersonNameComponentsFormatter()

let name =  "Mr. Steven Paul Jobs Jr."
// personNameComponents requires iOS (10.0 and later)
if let nameComps  = nameFormatter.personNameComponents(from: name) {
    nameComps.givenName    // Steven
    nameComps.middleName   // Paul
    nameComps.familyName   // Jobs
    nameComps.nameSuffix   // Jr.
    nameComps.namePrefix   // Mr.

    // It can also be convufgured to format your names
    // Default (same as medium), short, long or abbreviated

    nameFormatter.style = .default
    nameFormatter.string(from: nameComps)   // "Steven Jobs"

    nameFormatter.style = .short
    nameFormatter.string(from: nameComps)   // "Steven"

    nameFormatter.style = .long
    nameFormatter.string(from: nameComps)   // "Mr. Steven Paul Jobs jr."

    nameFormatter.style = .abbreviated
    nameFormatter.string(from: nameComps)   // SJ

    // It can also be use to return an attributed string using annotatedString method
    nameFormatter.style = .long
    nameFormatter.annotatedString(from: nameComps)   // "Mr. Steven Paul Jobs jr."
}




Swift 4

let words = "these words will be elements in an array".components(separatedBy: " ")



El problema del espacio en blanco

En general, las personas reinventan este problema y las malas soluciones una y otra vez. Es esto un espacio? "" y qué pasa con "\ n", "\ t" o algún carácter de espacio en blanco unicode que nunca hayas visto, en gran parte porque es invisible. Si bien puedes salirte con la tuya

Una solución débil

import Foundation
let pieces = "Mary had little lamb".componentsSeparatedByString(" ")

Si alguna vez necesita estrechar su control sobre la realidad, mire un video de la WWDC sobre cadenas o fechas. En resumen, casi siempre es mejor permitir que Apple resuelva este tipo de tarea mundana.

Solución robusta: use NSCharacterSet

La forma de hacer esto correctamente, en mi humilde opinión, es utilizar NSCharacterSet ya que, como se indicó anteriormente, su espacio en blanco podría no ser el que esperaba y Apple ha proporcionado un juego de caracteres en blanco. Para explorar los diversos conjuntos de caracteres proporcionados, consulte la documentación del desarrollador de NSCharacterSet de Apple y luego, solo entonces, aumente o construya un nuevo juego de caracteres si no se ajusta a sus necesidades.

NSCharacterSet espacios en blanco

Devuelve un conjunto de caracteres que contiene los caracteres en la Categoría general Z de Unicode y TABULACIÓN DE CARACTERES (U + 0009).

let longerString: String = "This is a test of the character set splitting system"
let components = longerString.components(separatedBy: .whitespaces)
print(components)



Xcode 8.0 / Swift 3

let fullName = "First Last"
var fullNameArr = fullName.components(separatedBy: " ")

var firstname = fullNameArr[0] // First
var lastname = fullNameArr[1] // Last

Largo camino:

var fullName: String = "First Last"
fullName += " " // this will help to see the last word

var newElement = "" //Empty String
var fullNameArr = [String]() //Empty Array

for Character in fullName.characters {
    if Character == " " {
        fullNameArr.append(newElement)
        newElement = ""
    } else {
        newElement += "\(Character)"
    }
}


var firsName = fullNameArr[0] // First
var lastName = fullNameArr[1] // Last



Swift 3

let line = "AAA    BBB\t CCC"
let fields = line.components(separatedBy: .whitespaces).filter {!$0.isEmpty}
  • Devuelve tres cadenas AAA , BBB y CCC
  • Filtra los campos vacíos
  • Maneja múltiples espacios y caracteres de tabulación
  • Si desea manejar nuevas líneas, reemplace .whitespaces con .whitespacesAndNewlines



Encontré un caso interesante, que

Método 1

var data:[String] = split( featureData ) { $0 == "\u{003B}" }

Cuando utilicé este comando para dividir algún símbolo de los datos que se cargaron desde el servidor , se puede dividir mientras se prueba en el simulador y se sincroniza con el dispositivo de prueba, pero no se divide en la aplicación de publicación, y Ad Hoc

Me lleva mucho tiempo hacer un seguimiento de este error, podría estar maldito por alguna versión Swift, o alguna versión iOS o ninguno

Tampoco se trata del código HTML, ya que intento stringByRemovingPercentEncoding y aún no funciona

Además 10/10/2015

en Swift 2.0 este método ha sido cambiado a

var data:[String] = featureData.split {$0 == "\u{003B}"}

método 2

var data:[String] = featureData.componentsSeparatedByString("\u{003B}")

Cuando utilicé este comando, puede dividir los mismos datos que cargan desde el servidor correctamente

Conclusión, realmente sugiero usar el método 2

string.componentsSeparatedByString("")



O sin cierres puedes hacer esto en Swift 2:

let fullName = "First Last"
let fullNameArr = fullName.characters.split(" ")
let firstName = String(fullNameArr[0])



La mayoría de estas respuestas asumen que la entrada contiene un espacio, no un espacio en blanco , y un solo espacio. Si puede hacer esa suposición con seguridad, entonces la respuesta aceptada (de bennett) es bastante elegante y también el método con el que iré cuando pueda.

Cuando no podemos hacer esa suposición, una solución más sólida debe cubrir los siguientes cambios que la mayoría de las respuestas aquí no consideran:

  • pestañas / nuevas líneas / espacios (espacios en blanco), incluidos los caracteres recurrentes
  • espacio en blanco inicial / final
  • Apple / Linux ( \n ) y Windows ( \r\n ) caracteres de nueva línea

Para cubrir estos casos, esta solución usa expresiones regulares para convertir todos los espacios en blanco (incluidos los caracteres recurrentes y de línea nueva de Windows) en un único espacio, recortar y luego dividir en un solo espacio:

Swift 3:

let searchInput = "  First \r\n \n \t\t\tMiddle    Last "
let searchTerms = searchInput 
    .replacingOccurrences(
        of: "\\s+",
        with: " ",
        options: .regularExpression
    )
    .trimmingCharacters(in: .whitespaces)
    .components(separatedBy: " ")

// searchTerms == ["First", "Middle", "Last"]



Tuve un escenario en el que varios caracteres de control pueden estar presentes en la cadena que quiero dividir. En lugar de mantener un conjunto de estos, simplemente dejo que Apple maneje esa parte.

Lo siguiente funciona con Swift 3.0.1 en iOS 10:

    let myArray = myString.components(separatedBy: .controlCharacters)



let str = "one two"
let strSplit = str.characters.split(" ").map(String.init) // returns ["one", "two"]

Xcode 7.2 (7C68)




Swift 2.2 Error Handling y capitalizedString Añadido:

func setFullName(fullName: String) {
    var fullNameComponents = fullName.componentsSeparatedByString(" ")

    self.fname = fullNameComponents.count > 0 ? fullNameComponents[0]: ""
    self.sname = fullNameComponents.count > 1 ? fullNameComponents[1]: ""

    self.fname = self.fname!.capitalizedString
    self.sname = self.sname!.capitalizedString
}



Swift 4 hace que sea mucho más fácil dividir caracteres, solo usa la nueva función de división para Strings.

Ejemplo:

let s = "hi, hello"
s.split(separator: ",")

Ahora tienes una matriz con 'hola' y 'hola'.




Esto ha cambiado nuevamente en Beta 5. ¡Weee! Ahora es un método en CollectionType

Antiguo:

var fullName = "First Last"
var fullNameArr = split(fullName) {$0 == " "}

Nuevo:

var fullName = "First Last"
var fullNameArr = fullName.split {$0 == " "}

Notas de lanzamiento de manzanas




Para Swift 2, XCode 7.1:

let complete_string:String = "Hello world"
let string_arr =  complete_string.characters.split {$0 == " "}.map(String.init)
let hello:String = string_arr[0]
let world:String = string_arr[1]



Según Swift 2.2

Simplemente escriba 2 líneas de código y obtendrá la cadena dividida.

let fullName = "FirstName LastName"
var splitedFullName = fullName.componentsSeparatedByString(" ")
print(splitedFullName[0])
print(splitedFullName[1]) 

Disfrutar. :)




Aquí hay un algoritmo que acabo de construir, que dividirá una String por cualquier Character de la matriz y, si existe el deseo de mantener las subcadenas con caracteres divididos, se podría establecer el parámetro de swallow en true .

Xcode 7.3 - Swift 2.2:

extension String {

    func splitBy(characters: [Character], swallow: Bool = false) -> [String] {

        var substring = ""
        var array = [String]()
        var index = 0

        for character in self.characters {

            if let lastCharacter = substring.characters.last {

                // swallow same characters
                if lastCharacter == character {

                    substring.append(character)

                } else {

                    var shouldSplit = false

                    // check if we need to split already
                    for splitCharacter in characters {
                        // slit if the last character is from split characters or the current one
                        if character == splitCharacter || lastCharacter == splitCharacter {

                            shouldSplit = true
                            break
                        }
                    }

                    if shouldSplit {

                        array.append(substring)
                        substring = String(character)

                    } else /* swallow characters that do not equal any of the split characters */ {

                        substring.append(character)
                    }
                }
            } else /* should be the first iteration */ {

                substring.append(character)
            }

            index += 1

            // add last substring to the array
            if index == self.characters.count {

                array.append(substring)
            }
        }

        return array.filter {

            if swallow {

                return true

            } else {

                for splitCharacter in characters {

                    if $0.characters.contains(splitCharacter) {

                        return false
                    }
                }
                return true
            }
        }
    }
}

Ejemplo:

"test text".splitBy([" "]) // ["test", "text"]
"test++text--".splitBy(["+", "-"], swallow: true) // ["test", "++" "text", "--"]



El manejo de cadenas sigue siendo un desafío en Swift y sigue cambiando de manera significativa, como se puede ver en otras respuestas. Afortunadamente las cosas se calman y se vuelve más simple. Esta es la manera de hacerlo con la versión 3.0 actual de Swift con múltiples caracteres separadores.

Swift 3:

let chars = CharacterSet(charactersIn: ".,; -")
let split = phrase.components(separatedBy: chars)

// Or if the enums do what you want, these are preferred. 
let chars2 = CharacterSet.alphaNumerics // .whitespaces, .punctuation, .capitalizedLetters etc
let split2 = phrase.components(separatedBy: chars2)



No encontré la solución que manejaría nombres con 3 o más componentes y admitir versiones anteriores de iOS.

struct NameComponentsSplitter {

    static func split(fullName: String) -> (String?, String?) {
        guard !fullName.isEmpty else {
            return (nil, nil)
        }
        let components = fullName.components(separatedBy: .whitespacesAndNewlines)
        let lastName = components.last
        let firstName = components.dropLast().joined(separator: " ")
        return (firstName.isEmpty ? nil : firstName, lastName)
    }
}

Pasó los casos de prueba:

func testThatItHandlesTwoComponents() {
    let (firstName, lastName) = NameComponentsSplitter.split(fullName: "John Smith")
    XCTAssertEqual(firstName, "John")
    XCTAssertEqual(lastName, "Smith")
}

func testThatItHandlesMoreThanTwoComponents() {
    var (firstName, lastName) = NameComponentsSplitter.split(fullName: "John Clark Smith")
    XCTAssertEqual(firstName, "John Clark")
    XCTAssertEqual(lastName, "Smith")

    (firstName, lastName) = NameComponentsSplitter.split(fullName: "John Clark Jr. Smith")
    XCTAssertEqual(firstName, "John Clark Jr.")
    XCTAssertEqual(lastName, "Smith")
}

func testThatItHandlesEmptyInput() {
    let (firstName, lastName) = NameComponentsSplitter.split(fullName: "")
    XCTAssertEqual(firstName, nil)
    XCTAssertEqual(lastName, nil)
}