arrays - dictionaries - swift array initialization
Cómo dividir una cadena por nuevas líneas en Swift (8)
Tengo una cadena que obtuve de un archivo de texto.
Archivo de texto:
Line 1
Line 2
Line 3
...
Quiero convertirlo en una matriz, un elemento de matriz por línea.
[ "Line 1", "Line 2", "Line 3", ... ]
Dependiendo de cómo se guardó el archivo, la cadena podría tomar una de las siguientes formas:
-
string = "Line 1/nLine 2/nLine 3/n..."
donde/n
es el nuevo carácter de línea (avance de línea) -
string = "Line 1/r/nLine 2/r/nLine 3/r/n..."
donde/r
es el carácter de retorno de carro.
Según tengo entendido,
/n
se usa comúnmente en Apple / Linux hoy, mientras que
/r/n
se usa en Windows.
¿Cómo divido una cadena en cualquier salto de línea para obtener una matriz de cadena sin elementos vacíos?
Actualizar
Hay varias soluciones que funcionan a continuación. En este punto, no tengo ninguna razón convincente para elegir una más correcta que las otras. Algunos factores que pueden influir en la elección podrían ser (1) cuán "veloz" es y (2) qué tan rápido es para cuerdas muy largas. Puede proporcionar comentarios votando uno o más de ellos y / o dejando un comentario.
Vea mi respuesta resumida aquí
¿Cómo divido una cadena en cualquier salto de línea para obtener una matriz de cadena sin elementos vacíos?
Ya casi estabas allí, es solo el cierre final que es diferente aquí:
let array = stringFromFile.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()).filter{!$0.isEmpty}
Que es lo mismo que:
let newLineChars = NSCharacterSet.newlineCharacterSet() // newline characters defined as (U+000A–U+000D, U+0085)
let array = stringFromFile.componentsSeparatedByCharactersInSet(newLineChars).filter{!$0.isEmpty}
ETA: se eliminaron los corchetes adicionales innecesarios en el cierre posterior
En Swift 2, la función de
split
nivel superior ahora es un método en
CollectionType
(al que se ajusta cada una de las "vistas de caracteres" de
String
).
Hay dos versiones del método, desea la que toma un cierre como predicado para indicar si un elemento dado debe tratarse como un separador.
Puede obtener la colección de caracteres de la cadena como una colección de caracteres
string.utf16
usando
string.utf16
, haciéndolos compatibles con las API
NSCharacterSet
.
De esta forma, podemos verificar fácilmente dentro del cierre si un determinado carácter de la cadena es miembro del conjunto de caracteres de nueva línea.
Vale la pena señalar que
split(_:)
devolverá una
SubSequence
de caracteres (básicamente un
Slice
), por lo que debe transformarse nuevamente en una matriz de Strings que generalmente es más útil.
He hecho esto a continuación usando
flatMap(String.init)
: el inicializador
UTF16View
en
String
está disponible, por lo que el uso de
flatMap
ignorará cualquier valor
nil
que pueda devolverse, asegurando que obtenga una matriz de cadenas no opcionales.
Entonces, para una forma agradable de hacer esto:
let str = "Line 1/nLine 2/r/nLine 3/n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lines = str.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
// lines = ["Line 1", "Line 2", "Line 3"]
Lo que hace que esto sea agradable es que el método de
split
tiene un parámetro
allowEmptySubsequences
, que garantiza que no recibas ninguna secuencia de caracteres vacía en el resultado.
Esto es
false
de forma predeterminada, por lo que no necesita especificarlo en absoluto.
Editar
Si desea evitar completamente
NSCharacterSet
, puede dividir fácilmente la colección de
Character
compatibles con Unicode.
let lines = str.characters.split { $0 == "/n" || $0 == "/r/n" }.map(String.init)
Swift puede tratar
"/r/n"
como un solo clúster de grafema extendido, usándolo como un solo
Character
para la comparación en lugar de crear una
String
.
También tenga en cuenta que el inicializador para crear una cadena a partir de un
Character
no está disponible, por lo que podemos usar el
map
.
Esta respuesta es un resumen de las otras soluciones ya dadas. Viene de mi respuesta más completa , pero sería útil tener las opciones de métodos reales disponibles aquí.
Las nuevas líneas generalmente se hacen con el carácter
/n
, pero también se pueden hacer con
/r/n
(de los archivos guardados en Windows).
Soluciones
1.
componentsSeparatedByCharactersInSet
let multiLineString = "Line 1/nLine 2/r/nLine 3/n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lineArray = multiLineString.componentsSeparatedByCharactersInSet(newlineChars).filter{!$0.isEmpty}
// "[Line 1, Line 2, Line 3]"
Si no se usara el
filter
,
/r/n
produciría un elemento de matriz vacío porque se cuenta como dos caracteres y, por lo tanto, separa la cadena dos veces en la misma ubicación.
2.
split
let multiLineString = "Line 1/nLine 2/r/nLine 3/n"
let newlineChars = NSCharacterSet.newlineCharacterSet()
let lineArray = multiLineString.utf16.split { newlineChars.characterIsMember($0) }.flatMap(String.init)
// "[Line 1, Line 2, Line 3]"
o
let multiLineString = "Line 1/nLine 2/r/nLine 3/n"
let lineArray = multiLineString.characters.split { $0 == "/n" || $0 == "/r/n" }.map(String.init)
// "[Line 1, Line 2, Line 3]"
Aquí
/r/n
se cuenta como un solo personaje Swift (un grupo de grafemas extendido)
3.
enumerateLines
let multiLineString = "Line 1/nLine 2/r/nLine 3/n"
var lineArray = [String]()
multiLineString.enumerateLines { (line, stop) -> () in
lineArray.append(line)
}
// "[Line 1, Line 2, Line 3]"
Para obtener más información sobre la sintaxis
enumerateLine
, consulte también
esta respuesta
.
Notas:
-
una cadena de varias líneas no suele mezclar tanto
/r/n
como/n
pero estoy haciendo esto aquí para mostrar que estos métodos pueden manejar ambos formatos. -
NSCharacterSet.newlineCharacterSet()
son caracteres de nueva línea definidos como (U + 000A – U + 000D, U + 0085), que incluyen/r
y/n
. - Esta respuesta es un resumen de las respuestas a mi pregunta anterior . Lea esas respuestas para más detalles.
Para el registro, Swift''s Foundation
CharacterSet
se puede usar dentro de la división:
alternativa 1
extension String {
var lines: [String] {
return split { String($0).rangeOfCharacter(from: .newlines) != nil }.map(String.init)
}
}
alternativa 2
extension String {
var lines: [String] {
return split { CharacterSet.newlines.contains($0.unicodeScalars.first!) }.map(String.init)
}
}
en Xcode 8.2, Swift 3.0.1:
Utilice los componentes del método NSString (separados por :)
let text = "line1/nline2"
let array = text.components(separatedBy: CharacterSet.newlines)
O use el método String
enumerateLines
, como la respuesta de
Leo Dabus
Swift 4:
Recomendaría guardar primero su CSV en una cadena si aún no lo ha hecho, luego "limpiar" la cadena eliminando retornos de carro innecesarios
let dataString = String(data: yourData!, encoding: .utf8)!
var cleanFile = dataString.replacingOccurrences(of: "/r", with: "/n")
cleanFile = cleanFile.replacingOccurrences(of: "/n/n", with: "/n")
Lo anterior le dará una cadena con el formato más deseable, luego puede separar la cadena usando / n como su separador:
let csvStrings = cleanFile.components(separatedBy: ["/n"])
Ahora tiene una serie de 3 elementos como:
["Línea1", "Línea2", "Línea3"]
Estoy usando un archivo CSV y después de hacer esto, estoy dividiendo los elementos en componentes, así que si sus elementos fueran algo como:
["Line1, Line2, Line3", "LineA, LineB, LineC"]
let component0 = csvStrings[0].components(separatedBy: [","]) // ["Line1","Line2","Line3"]
let component1 = csvStrings[1].components(separatedBy: [","]) // ["LineA","LineB","LineC"]
Swift 5 o posterior
Puede dividir su
String
utilizando la nueva propiedad de
Character
isNewline
:
let sentence = "Line 1/nLine 2/nLine 3/n"
var lines = sentence.split { $0.isNewline }
print(lines) // "[Line 1, Line 2, Line 3]"
Respuesta original
Puede usar el método de cadena enumerateLines :
Enumera todas las líneas de una cadena.
Swift 3 o posterior
let sentence = "Line 1/nLine 2/nLine 3/n"
var lines: [String] = []
sentence.enumerateLines { line, _ in
lines.append(line)
}
print(lines) // "[Line 1, Line 2, Line 3]"
extension String {
var lines: [String] {
var result: [String] = []
enumerateLines { line, _ in result.append(line) }
return result
}
}
Uso:
let sentence2 = "Line 4/nLine 5/nLine 6/n"
let sentence2Lines = sentence2.lines
print(sentence2Lines) // ["Line 4", "Line 5", "Line 6"]
let sentence3 = "Line 7/r/nLine 8/r/nLine 9/r/n"
let sentence3Lines = sentence3.lines
print(sentence3Lines) // "[Line 7, Line 8, Line 9]"
let test1 = "Line1/n/rLine2/nLine3/rLine4"
let t1 = test1.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
let t2 = t1.filter{ $0 != "" }
let t3 = t1.filter{ !$0.isEmpty }