After reading the advice here, I came up with some code. I really like scanners, so I made something similar for Data objects. I process my legacy data starting on line 31.
struct DataScanner {
var index: Int
let data: Data
var isAtEnd: Bool {
return index == data.count
}
init(data: Data) {
self.data = data
index = 0
}
mutating func scanByesInRange(_ range: Range) -> Array {
var bytes: Array = []
while index < data.count && range.contains(data[index]) {
bytes.append(data[index])
index += 1
}
return bytes
}
mutating func scanUpToByesInRange(_ range: Range) -> Array {
var bytes: Array = []
while index < data.count && range.contains(data[index]) == false {
bytes.append(data[index])
index += 1
}
return bytes
}
}
func linesFrom(legacyData: Data) -> Array<(Data, String)> {
var array: Array<(Data, String)> = []
var reader = DataScanner(data: legacyData)
let asciiRange = UInt8(0x0) ..< UInt8(0x80)
while reader.isAtEnd == false {
let metadata = reader.scanUpToByesInRange(asciiRange)
let textData = reader.scanByesInRange(asciiRange)
array.append((
Data(metadata),
textData.reduce("") {(s, b) -> String in
s + (String(UnicodeScalar(b)))
}))
}
return array
}
This gives me an array of tuples, each of which pairs some metadata with a text string. So far it works well on the legacy files I have. But if I've made any more blunders, please let me know!
One thing I'm curious about: I can instantiate a Scanner as a let constant. But when I do this with my data scanner, I get a "Cannot use mutating member on immutable value" error when I try to use it. I know it's a minor point, but is there some way of defining DataScanner so that I can instatiate it with let?