【发布时间】:2016-06-27 18:21:57
【问题描述】:
我正在尝试对 Swift3 Data 对象进行一些简单的 BSON 解析。我觉得我在与系统作斗争。
让我们从一些输入和一个方案开始:
let input = Data(bytes: [2, 0x20, 0x21, 3, 0x30, 0x31, 0x32, 1, 0x10, 4, 0x40, 0x41, 0x42, 0x43])
这只是一个简单的数据流,轻率的方案是前导字节指示后面有多少字节组成下一个块。所以在上面,前面的 2 表示 0x20、0x21 是第一个块,后面是一个 3 字节的块,包含字节 0x30、0x31、0x32 等。
流
我的第一个想法是使用流(呃、生成器、迭代器等)来实现。所以我最终得到了类似的东西:
var iter = input.makeIterator()
func parse(_ stream:inout IndexingIterator<Data>) -> Data {
var result = Data()
if let count = stream.next() {
for _ in 0..<count {
result.append(Data(bytes:[stream.next()!]))
}
}
return result
}
parse(&iter)
parse(&iter)
parse(&iter)
parse(&iter)
这导致了多个问题/意见:
1) 为什么有人会let 一个迭代器?这件事的全部意义在于跟踪集合中不断变化的位置。我真的很困惑为什么 Swift 作者选择将迭代器发送到“所有的值语义”的老鼠洞。这意味着我必须将inout's 放在我所有的解析函数上。
2) 我觉得我用 IndexingIterator 过度指定了参数类型。也许我只是需要习惯冗长的泛型?
Python Struct'esque
对这种方法感到沮丧,我想我可能会模仿 python 的 struct.unpack() 样式,其中返回解析数据的元组以及未使用的数据。因为据说只要我不改变数据,数据就是神奇而高效的。结果是这样的:
func parse2(_ data:Data) -> (Data, Data) {
let count = Int(data[0])
return (data.subdata(in: 1..<count+1), data.subdata(in: count+1..<data.count))
}
var remaining = input
var chunk = Data()
(chunk, rest) = parse2(remaining)
chunk
(chunk, rest) = parse2(remaining)
chunk
(chunk, rest) = parse2(remaining)
chunk
(chunk, rest) = parse2(remaining)
chunk
我遇到了两个问题。
1) 我真正想要返回的是data[1..count], data.subdata(in: count+1..<data.count)。但这会返回一个 MutableRandomAccessSlice。这似乎是一种完全不同的类型?所以我最终使用了更多参与的subdata。
2) 可以使用封闭范围为 Data 下标,但 subdata 方法只会采用开放范围。那是怎么回事?
公开叛乱,旧习惯开始
现在很烦这个老 Smalltalker 似乎在这里找不到快乐,我只是自己动手:
class DataStream {
let data:Data
var index = 0
var atEnd:Bool {
return index >= self.data.count
}
init(data:Data) {
self.data = data
}
func next() -> UInt8 {
let byte = self.data[self.index]
self.index += 1
return byte
}
func next(_ count:Int) -> Data {
let subdata = self.data.subdata(in: self.index..<self.index + count)
self.index += count
return subdata
}
}
func parse3(_ stream:DataStream) -> Data {
let count = Int(stream.next())
return stream.next(count)
}
let stream = DataStream(data: input)
parse3(stream)
parse3(stream)
parse3(stream)
parse3(stream)
从最终使用 POV 来看,我对这个解决方案很满意。我可以充实 DataStream 来做各种事情。但是......我现在已经走出了人迹罕至的道路,感觉我没有“得到它”(Swiftish 灯泡)。
TL;DR 版本
在玩了这么多之后,我发现自己很好奇在数据结构中流式传输的最惯用方式是什么,根据其中遇到的内容从中提取数据。
【问题讨论】:
-
#2 你可以使用类型别名
-
我能看出您的沮丧,但 Xcode 8 仍处于测试阶段。在 Xcode 7 Beta 4 或 5 之前,Swift 2 并不是超级稳定
-
@CodeDifferent 我不确定这与 xcode8 或 swift3 有多大关系,除了假设的方向是使用
Data优先于[UInt8]s 前进。我感到沮丧的一个主要因素是为什么迭代器作为值类型结构而不是引用类型对象更好。 -
另一种选择是从数据中创建一个InputStream,然后使用流读取方法。 – 但是您的 DataStream 也是一个好方法。我会更改一些细节,例如检查是否有足够的数据可用,否则返回
nil。 -
希望您的评论是在@MartinR 的答案中;带有您建议的 DataStream 是我最终使用并满意的解决方案。
标签: swift3 swift-data