wake-up-neo.com

So teilen Sie eine Zeichenfolge in Swift durch neue Zeilen auf

Ich habe eine Zeichenfolge, die ich aus einer Textdatei erhalten habe.

Textdatei:

Line 1
Line 2
Line 3
...

Ich möchte es in ein Array umwandeln, ein Arrayelement pro Zeile. 

[ "Line 1", "Line 2", "Line 3", ... ]

Je nachdem, wie die Datei gespeichert wurde, kann die Zeichenfolge eine der folgenden Formen annehmen:

  • string = "Line 1\nLine 2\nLine 3\n..." Dabei ist \n das neue Zeilenzeichen (Zeilenvorschub)

  • string = "Line 1\r\nLine 2\r\nLine 3\r\n..." Dabei ist \r das Wagenrücklaufzeichen. 

Soweit ich es verstehe, wird \n heutzutage allgemein in Apple/Linux verwendet, während \r\n in Windows verwendet wird. 

Wie kann ich einen String bei einem Zeilenumbruch aufteilen, um ein String-Array ohne leere Elemente zu erhalten?

Aktualisieren

Es gibt mehrere Lösungen, die unten funktionieren. Zu diesem Zeitpunkt habe ich keinen zwingenden Grund, einen als korrekter zu wählen als die anderen. Einige Faktoren, die die Auswahl beeinflussen können, könnten sein (1) wie "schnell" ist und (2) wie schnell sie für sehr lange Saiten sind. Sie können Feedback geben, indem Sie eine oder mehrere von ihnen aktivieren und/oder einen Kommentar hinterlassen.

Siehe meine zusammengefasste Antwort hier

27
Suragch

Sie können die String-Methode enumerateLines verwenden:

Zählt alle Zeilen in einer Zeichenfolge auf.

Swift 3 oder später

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

Verwendungszweck:

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]"
45
Leo Dabus

in Xcode 8.2, Swift 3.0.1:

Verwenden Sie die NSString-Methode components (separatesBy :)

let text = "line1\nline2"
let array = text.components(separatedBy: CharacterSet.newlines)

Oder verwenden Sie die String-Methode enumerateLines wie die Antwort von Leo Dabus

18
zagger

In Swift 2 ist die split-Funktion auf oberster Ebene jetzt eine Methode für CollectionType (die jeder der "Zeichenansichten" von Strings entspricht). Es gibt zwei Versionen der Methode. Sie möchten, dass die eine Schließung als Vergleichselement verwendet wird, um anzugeben, ob ein bestimmtes Element als Trennzeichen behandelt werden soll.

Sie können die Zeichensammlung aus der Zeichenfolge als eine Sammlung von UTF16-Zeichen mit string.utf16 abrufen, um sie mit den NSCharacterSet-APIs kompatibel zu machen. Auf diese Weise können wir innerhalb des Abschlusses leicht überprüfen, ob ein bestimmtes Zeichen in der Zeichenfolge ein Mitglied des Newline-Zeichensatzes ist.

Es ist erwähnenswert, dass split(_:) eine SubSequence von Zeichen zurückgibt (im Wesentlichen eine Slice). Daher muss sie wieder in ein Array von Strings umgewandelt werden, was im Allgemeinen nützlicher ist. Ich habe dies im Folgenden mit flatMap(String.init) ausgeführt - der UTF16View-Initialisierer für String ist fehlgeschlagen. Wenn Sie flatMap verwenden, werden alle nil-Werte ignoriert, die zurückgegeben werden.

Also für einen Nice Swift-ähnlichen Weg:

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"]

Das Schöne daran ist, dass die split-Methode einen Parameter allowEmptySubsequences besitzt, der sicherstellt, dass Sie keine leeren Zeichenfolgen im Ergebnis erhalten. Dies ist standardmäßig false, so dass Sie es überhaupt nicht angeben müssen.

Bearbeiten

Wenn Sie NSCharacterSet vollständig vermeiden möchten, können Sie die Sammlung von Unicode-kompatiblen Characters genauso einfach aufteilen.

let lines = str.characters.split { $0 == "\n" || $0 == "\r\n" }.map(String.init)

Swift kann "\r\n" als einen einzelnen erweiterten Graphem-Cluster behandeln, indem es als einzelne Character für den Vergleich verwendet wird, anstatt eine String zu erstellen. Beachten Sie auch, dass der Initialisierer zum Erstellen einer Zeichenfolge aus einer Character nicht fehlgeschlagen ist. Daher können Sie einfach map verwenden.

9
Stuart
let test1 = "Line1\n\rLine2\nLine3\rLine4"
let t1 = test1.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet())
let t2 = t1.filter{ $0 != "" }
let t3 = t1.filter{ !$0.isEmpty }
6
user3441734

Diese Antwort ist eine Zusammenfassung der anderen Lösungen. Es kommt von meiner volleren Antwort , aber es wäre nützlich, wenn Sie die tatsächlichen Methodenauswahlmöglichkeiten zur Verfügung haben. 

Neue Zeilen werden normalerweise mit dem Zeichen \n erstellt, können jedoch auch mit \r\n (aus in Windows gespeicherten Dateien) erstellt werden.

Lösungen

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]"

Wenn filter nicht verwendet wird, würde \r\n ein leeres Array-Element erzeugen, da es als zwei Zeichen gezählt wird und die Zeichenfolge an derselben Stelle zweimal trennt.

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]"

oder 

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]"

Hier wird \r\n als einzelnes Swift-Zeichen gezählt (ein erweiterter Graphem-Cluster). 

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]"

Weitere Informationen zur enumerateLine-Syntax finden Sie unter diese Antwort auch.

Anmerkungen:

  • ein mehrzeiliger String würde normalerweise nicht beides \r\n und \n mischen, aber ich mache dies hier, um zu zeigen, dass diese Methoden beide Formate verarbeiten können.
  • NSCharacterSet.newlineCharacterSet() sind Zeilenumbrüche, die als (U + 000A – U + 000D, U + 0085) definiert sind und \r und \n enthalten.
  • Diese Antwort ist eine Zusammenfassung der Antworten auf meine vorherige Frage . Lesen Sie diese Antworten für weitere Einzelheiten.
5
Suragch

Für die Aufzeichnung kann Swift's Foundation CharacterSet innerhalb von split verwendet werden:

alternative 1

extension String {
    var lines: [String] {
        return split { String($0).rangeOfCharacter(from: .newlines) != nil }.map(String.init)
    }
}

alternative 2

extension String {
    var lines: [String] {
        return split { CharacterSet.newlines.contains($0.unicodeScalars.first!) }.map(String.init)
    }
}
3
Cœur

Wie kann ich einen String bei einem Zeilenumbruch aufteilen, um ein String-Array ohne leere Elemente zu erhalten?

Sie waren fast da - es ist nur der hintere Verschluss, der hier anders ist:

let array = stringFromFile.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()).filter{!$0.isEmpty}

Welches ist das Gleiche wie:

let newLineChars = NSCharacterSet.newlineCharacterSet() // newline characters defined as (U+000A–U+000D, U+0085)
let array = stringFromFile.componentsSeparatedByCharactersInSet(newLineChars).filter{!$0.isEmpty}

ETA: Unnötige zusätzliche Halterungen am nachlaufenden Verschluss entfernt

1
simons

Swift 4:

Ich würde empfehlen, zuerst Ihre CSV-Datei in einem String zu speichern, wenn Sie es noch nicht getan haben, und dann den String "zu reinigen", indem Sie nicht benötigte Zeilenumbrüche entfernen

        let dataString = String(data: yourData!, encoding: .utf8)!

        var cleanFile = dataString.replacingOccurrences(of: "\r", with: "\n")
        cleanFile = cleanFile.replacingOccurrences(of: "\n\n", with: "\n")

Oben erhalten Sie eine Zeichenfolge mit einem äußerst wünschenswerten Format. Dann können Sie die Zeichenfolge mit\n als Trennzeichen trennen:

        let csvStrings = cleanFile.components(separatedBy: ["\n"])

Jetzt haben Sie ein Array von 3 Elementen wie:

["Line1", "Line2", "Line3"]

Ich verwende eine CSV-Datei, und nachdem ich das getan habe, teile ich Artikel in Komponenten auf. Wenn Ihre Artikel so aussehen:

["Line1, Line2, Line3", "LineA, LineB, LineC"]

        let component0 = csvStrings[0].components(separatedBy: [","]) // ["Line1","Line2","Line3"]
        let component1 = csvStrings[1].components(separatedBy: [","]) // ["LineA","LineB","LineC"]
0
Alan Gonzalez