How to split a string by new lines in Swift

后端 未结 9 2314
一个人的身影
一个人的身影 2020-11-29 06:59

I have a string that I got from a text file.

Text file:

Line 1
Line 2
Line 3
...

I want to convert it to an array, one array element p

相关标签:
9条回答
  • 2020-11-29 07:31

    In Swift 2, the top-level split function is now a method on CollectionType (which each of Strings "character views" conforms to). There are two versions of the method, you want the one that takes a closure as a predicate to indicate whether a given element should be treated as a separator.

    You can get the character collection from the string as a collection of UTF16 characters using string.utf16, making them compatible with the NSCharacterSet APIs. This way, we can easily check inside the closure whether a given character in the string is a member of the newline character set.

    It's worth noting that split(_:) will return a SubSequence of characters (basically a Slice), so it needs transforming back into an array of Strings which is generally more useful. I've done this below using flatMap(String.init) - the UTF16View initialiser on String is failable, so using flatMap will ignore any nil values that might be returned, ensuring you get an array of non-optional strings back.

    So for a nice Swift-like way of doing this:

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

    What makes this nice is that the split method has a parameter allowEmptySubsequences, which ensures you don't receive any empty character sequences in the result. This is false by default, so you don't actually need to specify it at all.

    Edit

    If you want to avoid NSCharacterSet altogether, you can just as easily split the collection of unicode compliant Characters.

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

    Swift is able to treat "\r\n" as a single extended grapheme cluster, using it as a single Character for the comparison instead of creating a String. Also note that the initialiser for creating a string from a Character is non failable, so we can just use map.

    0 讨论(0)
  • 2020-11-29 07:43

    Swift 5.2 or later

    You can split your String using the new Character property isNewline:

    let sentence = "Line 1\nLine 2\nLine 3\n"
    let lines = sentence.split(whereSeparator: \.isNewline)
    print(lines)   // "[Line 1, Line 2, Line 3]\n"
    

    You can also extend StringProtocol and create a lines property to return break up the string lines into subsequences:

    extension StringProtocol {
        var lines: [SubSequence] { split(whereSeparator: \.isNewline) }
    }
    

    let sentence = "Line 1\nLine 2\r\nLine 3\n"
    for line in sentence.lines {
        print(line)
    }
    let lines = sentence.lines  // ["Line 1", "Line 2", "Line 3"]
    


    Original Answer

    You can use String method enumerateLines:

    Enumerates all the lines in a string.

    Swift 3 or later

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

    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]"
    
    0 讨论(0)
  • 2020-11-29 07:43

    How do I split a string at any line break to get a String array without any empty elements?

    You were almost there - it's just the trailing closure which is different here:

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

    Which is the same as:

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

    ETA: removed unnecessary extra brackets at trailing closure

    0 讨论(0)
  • 2020-11-29 07:46
         let getName = "Davender+Verma"
         let cleanFile = getName.replacingOccurrences(of: "+", with: "+\n")
         self.upcomingViewPetName.text = cleanFile
    
    
         Output: Davender+
                verma
    
    Or 
         let getName = "Davender+Verma"
         let cleanFile = getName.replacingOccurrences(of: "+", with: "\n")
         self.upcomingViewPetName.text = cleanFile
    
    Output:     Davender
                verma
    
    0 讨论(0)
  • 2020-11-29 07:47

    This answer is a summary of the other solutions already given. It comes from my fuller answer, but it would be useful to have the actual method choices available here.

    New lines are usually made with the \n character, but can also be made with \r\n (from files saved in Windows).

    Solutions

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

    If filter were not used, then \r\n would produce an empty array element because it gets counted as two characters and so separates the string twice at the same location.

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

    or

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

    Here \r\n gets counted as a single Swift character (an extended grapheme 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]"
    

    For more about the enumerateLine syntax, see this answer also.

    Notes:

    • a multi line string would not usually mix both \r\n and \n but I am doing this here to show that these methods can handle both formats.
    • NSCharacterSet.newlineCharacterSet() are newline characters defined as (U+000A–U+000D, U+0085), which include \r and \n.
    • This answer is a summary of the answers to my previous question. Read those answers for more detail.
    0 讨论(0)
  • 2020-11-29 07:55

    For the record, Swift's Foundation CharacterSet can be used within split:

    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)
        }
    }
    
    0 讨论(0)
提交回复
热议问题