Skip to content

Instantly share code, notes, and snippets.

@burnto
Created September 25, 2014 05:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save burnto/d66494d8523c8a01f624 to your computer and use it in GitHub Desktop.
Save burnto/d66494d8523c8a01f624 to your computer and use it in GitHub Desktop.
import Foundation
let ScanLines = RegExScanFn("\n|\r|\r\n")
let ScanWords = RegExScanFn("\\s")
func RegExScanFn(regex : String) -> ((String) -> (String, String?)) {
return { (s : String) -> (String, String?) in
let ns = s as NSString
let location = ns.rangeOfString(regex, options: .RegularExpressionSearch).location
if location != NSNotFound {
return (ns.substringToIndex(location), ns.substringFromIndex(location + 1))
} else {
return (s, nil)
}
}
}
class Scanner : SequenceType {
var filePath : String
var splitFn : (String) -> (String, String?)
var bufferSize : Int
init(filePath : String, bufferSize : Int = 128, splitFn : (String) -> (String, String?)) {
self.filePath = filePath
self.bufferSize = bufferSize
self.splitFn = splitFn
}
func generate() -> ScannerGenerator {
return ScannerGenerator(filePath: filePath, bufferSize: bufferSize, splitFn)
}
}
class ScannerGenerator : GeneratorType {
var stream : NSInputStream
var byteBufferSize : Int
var byteBuffer : UnsafeMutablePointer<UInt8>
var stringBuffer : NSString
var splitFn : (String) -> (String, String?)
init(filePath : String, bufferSize : Int, splitFn : (String) -> (String, String?)) {
self.stream = NSInputStream.inputStreamWithFileAtPath(filePath)
self.byteBufferSize = bufferSize + 1
self.splitFn = splitFn
byteBuffer = UnsafeMutablePointer<UInt8>.alloc(byteBufferSize)
stringBuffer = ""
stream.open()
}
deinit {
stream.close()
}
func next() -> String? {
if stream.streamStatus != .Open {
return nil
}
while true {
let (s1, s2) : (String, String?) = splitFn(stringBuffer)
if (s2 != nil) {
stringBuffer = s2!
return s1
}
var bytesRead = stream.read(byteBuffer, maxLength: byteBufferSize - 1)
if bytesRead < 0 {
stream.close()
return nil
}
if bytesRead == 0 {
break
}
var charBuffer = UnsafeMutablePointer<CChar>(byteBuffer)
charBuffer[bytesRead] = 0 // null terminate the c-string
if let utf8String = String.fromCString(charBuffer) {
stringBuffer = stringBuffer + utf8String
} else {
return nil
}
}
stream.close()
return stringBuffer
}
}
import XCTest
class ScannerTests: XCTestCase {
var filePath : String = ""
override func setUp() {
super.setUp()
filePath = NSBundle(forClass: self.dynamicType).pathForResource("TestFile1", ofType: "txt")!
}
override func tearDown() {
super.tearDown()
}
func testScanLines() {
var reader = Scanner(filePath: filePath, splitFn: ScanLines)
let g = reader.generate()
XCTAssert(g.next() == "Aa")
XCTAssert(g.next() == "Bb")
XCTAssert(g.next() == "Cc Dd")
XCTAssert(g.next() == nil)
}
func testScanLinesSmallBuffer() {
var reader = Scanner(filePath: filePath, bufferSize: 1, splitFn: ScanLines)
let g = reader.generate()
XCTAssert(g.next() == "Aa")
XCTAssert(g.next() == "Bb")
XCTAssert(g.next() == "Cc Dd")
XCTAssert(g.next() == nil)
}
func testScanWords() {
var reader = Scanner(filePath: filePath, bufferSize: 1, splitFn: ScanWords)
let g = reader.generate()
XCTAssert(g.next() == "Aa")
XCTAssert(g.next() == "Bb")
XCTAssert(g.next() == "Cc")
XCTAssert(g.next() == "Dd")
XCTAssert(g.next() == nil)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment