It’s easy enough in Swift to get an array of strings representing the lines of a text file:
let contents = try! String(contentsOf: textFileURL) let lines = contents.components(separatedBy: .newlines)
…but what if the input might be a DOS format text file? CharacterSet.newlines
contains both "\r"
and "\n"
, so the code above will divide the text at each of them, and the array of strings will contain an extra empty string between every line.
Turns Out™ that Foundation has a number of APIs that understand all the ways line endings are represented in text files. The easiest way to read a text file with arbitrary line endings is to use the enumerateSubstrings(in:options:body:)
method, specifying EnumerationOptions.byLines
. This uses getLineStart(_:end:contentsEnd:for:)
to recognize the line endings (see the documentation thereof for details) and is powerful enough to handle files with mixed line endings.
(In Objective-C these are the -enumerateSubstringsInRange:options:usingBlock:
and -getLineStart:end:contentsEnd:forRange:
methods of NSString
.)
It’s straightforward to wrap this into an extension on String
:
extension String { | |
func separatedIntoLines() -> [String] { | |
var lines: [String] = [] | |
let wholeString = self.startIndex..<self.endIndex | |
self.enumerateSubstrings(in: wholeString, options: .byLines) { | |
(substring, range, enclosingRange, stopPointer) in | |
if let line = substring { | |
lines.append(line) | |
} | |
} | |
return lines | |
} | |
} |
The name separatedIntoLines()
seems like the best choice to describe the work being done; the alternative of a computed property simply called lines
seems a little too terse to me.