Created
August 30, 2021 11:01
-
-
Save JanX2/d000df425f251e7553149b1f838378a4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/// Adapted from https://stackoverflow.com/a/62112007 | |
/// https://stackoverflow.com/questions/31778700/read-a-text-file-line-by-line-in-swift | |
extension URL { | |
/// Note: Only supports newline as the line break character. | |
func processLineByLine(processLine: @escaping (_ line: String) -> ()) { | |
/// Make sure the file exists. | |
guard FileManager.default.fileExists(atPath: self.path) else { | |
preconditionFailure("file expected at \(self.absoluteString) is missing") | |
} | |
/// Open the file for reading. | |
/// Note: user should be prompted the first time to allow reading from this location. | |
guard let filePointer: UnsafeMutablePointer<FILE> = fopen(self.path, "r") else { | |
preconditionFailure("Could not open file at \(self.absoluteString)") | |
} | |
defer { | |
/// Remember to close the file when done. | |
fclose(filePointer) | |
} | |
/// A pointer to a null-terminated, UTF-8 encoded sequence of bytes. | |
var lineByteArrayPointer: UnsafeMutablePointer<CChar>? = nil | |
/// The smallest multiple of 16 that will fit the byte array for this line. | |
var lineCap: Int = 0 | |
/// Define nextLine as closure, because we need to use it twice. | |
let nextLine = { | |
/// For details regarding `getline()` have a look at | |
/// https://www.man7.org/linux/man-pages/man3/getline.3.html | |
return getline(&lineByteArrayPointer, &lineCap, filePointer) | |
} | |
defer { | |
/// If *lineptr is set to NULL before the call, then getline() will | |
/// allocate a buffer for storing the line. This buffer should be | |
/// freed by the user program even if getline() failed. | |
lineByteArrayPointer?.deallocate() | |
} | |
/// Initial iteration. | |
var bytesRead = nextLine() | |
while (bytesRead > 0) { | |
guard let linePointer = lineByteArrayPointer else { | |
break | |
} | |
/// Note: this translates the sequence of bytes to a string | |
/// using the UTF-8 encoding for interpreting byte sequences. | |
var lineString = String.init(cString: linePointer) | |
/// `lineString` initially includes the newline character, if one was found. | |
if lineString.last?.isNewline == true { | |
lineString = String(lineString.dropLast()) | |
} | |
/// Process this single line of text. | |
processLine(lineString) | |
/// Update number of bytes read and the pointers for the next iteration. | |
bytesRead = nextLine() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment