Skip to content

Instantly share code, notes, and snippets.

@klaas
Last active May 21, 2017 14:51
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save klaas/4691612802f540b6a9c5 to your computer and use it in GitHub Desktop.
Save klaas/4691612802f540b6a9c5 to your computer and use it in GitHub Desktop.
Xcode 6.1 version of source for this SO answer http://stackoverflow.com/a/24648951/292145
import Foundation
class StreamReader {
let encoding : UInt
let chunkSize : Int
var atEof : Bool = false
var fileHandle : NSFileHandle!
let buffer : NSMutableData!
let delimData : NSData!
init?(path: String, delimiter: String = "\n", encoding: UInt = NSUTF8StringEncoding, chunkSize : Int = 4096) {
self.chunkSize = chunkSize
self.encoding = encoding
self.fileHandle = NSFileHandle(forReadingFromURL: NSURL(fileURLWithPath: path)!, error: nil)
if self.fileHandle == nil {
return nil
}
self.buffer = NSMutableData(capacity: chunkSize)!
// Create NSData object containing the line delimiter:
delimData = delimiter.dataUsingEncoding(NSUTF8StringEncoding)!
}
deinit {
self.close()
}
/// Return next line, or nil on EOF.
func nextLine() -> String? {
if atEof {
return nil
}
// Read data chunks from file until a line delimiter is found:
var range = buffer.rangeOfData(delimData, options: NSDataSearchOptions(0), range: NSMakeRange(0, buffer.length))
while range.location == NSNotFound {
var tmpData = fileHandle.readDataOfLength(chunkSize)
if tmpData.length == 0 {
// EOF or read error.
atEof = true
if buffer.length > 0 {
// Buffer contains last line in file (not terminated by delimiter).
let line = NSString(data: buffer, encoding: encoding);
buffer.length = 0
return line
}
// No more lines.
return nil
}
buffer.appendData(tmpData)
range = buffer.rangeOfData(delimData, options: NSDataSearchOptions(0), range: NSMakeRange(0, buffer.length))
}
// Convert complete line (excluding the delimiter) to a string:
let line = NSString(data: buffer.subdataWithRange(NSMakeRange(0, range.location)),
encoding: encoding)
// Remove line (and the delimiter) from the buffer:
buffer.replaceBytesInRange(NSMakeRange(0, range.location + range.length), withBytes: nil, length: 0)
return line
}
/// Start reading from the beginning of file.
func rewind() -> Void {
fileHandle.seekToFileOffset(0)
buffer.length = 0
atEof = false
}
/// Close the underlying file. No reading must be done after calling this method.
func close() -> Void {
if fileHandle != nil {
fileHandle.closeFile()
fileHandle = nil
}
}
}
extension StreamReader : SequenceType {
func generate() -> GeneratorOf<String> {
return GeneratorOf<String> {
return self.nextLine()
}
}
}
@concertman
Copy link

Thanks so much for sharing this. Is there a license associated with this code?

@petershaw
Copy link

Cool. Thanks a lot for the code. Is it possible to get a nextChar() function, too. How to implement a method that just returns one character?

@johndpope
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment