Swift3 解析swift(3)数据流的惯用方法

Swift3 解析swift(3)数据流的惯用方法,swift3,swift-data,Swift3,Swift Data,我正在尝试对Swift3数据对象进行一些简单的BSON解析。我觉得我在和体制抗争 让我们从一些输入和一个方案开始: let input = Data(bytes: [2, 0x20, 0x21, 3, 0x30, 0x31, 0x32, 1, 0x10, 4, 0x40, 0x41, 0x42, 0x43]) 这只是一个简单的数据流,轻率的方案是一个前导字节指示下一个数据块后面有多少字节。因此,在上面,前导的2表示0x20、0x21是第一个块,后面是一个包含字节0x30、0x31、0x32等的

我正在尝试对Swift3数据对象进行一些简单的BSON解析。我觉得我在和体制抗争

让我们从一些输入和一个方案开始:

let input = Data(bytes: [2, 0x20, 0x21, 3, 0x30, 0x31, 0x32, 1, 0x10, 4, 0x40, 0x41, 0x42, 0x43])
这只是一个简单的数据流,轻率的方案是一个前导字节指示下一个数据块后面有多少字节。因此,在上面,前导的2表示0x20、0x21是第一个块,后面是一个包含字节0x30、0x31、0x32等的3字节块

我的第一个想法是使用流(呃,生成器,迭代器,等等)来完成。所以我最终得到了这样的结果:

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) 我真正想要返回的是
data[1..count],data.subdata(in:count+1..=self.data.count)
}
init(数据:数据){
self.data=数据
}
func next()->UInt8{
让byte=self.data[self.index]
自索引+=1
返回字节
}
func next(u计数:Int)->数据{
让subdata=self.data.subdata(in:self.index..data{
让count=Int(stream.next())
返回流。下一个(计数)
}
let stream=DataStream(数据:输入)
parse3(流)
parse3(流)
parse3(流)
parse3(流)
这个解决方案从最终用户的角度来看我很满意。我可以充实DataStream来做各种事情。但是……我现在已经走上了老路,感觉自己没有“得到它”(快速的灯泡)

TL;DR版本

太长了,读不下去了,我发现自己很好奇,从数据结构中提取数据的最惯用的方法是:根据他们遇到的情况,从数据中提取数据。这并不能真正回答OP:s的问题,但我会留下它,因为它可能会给讨论增添一些有趣的内容;

split
这里不生成流,而是生成一块数据序列)

这有点类似于您的“Python结构式”解决方案;您可以使用
Data
split
方法的
isSeparator
谓词闭包来解析字节流

Swift 3
数据
结构的未测试等效代码段:

let input = Data(bytes: [2, 0x20, 0x21, 3, 0x30, 0x31, 0x32, 1, 0x10, 4, 0x40, 0x41, 0x42, 0x43])

var isSep = true
var byteCounter: (UInt8, UInt8) = (0,0)
let parsed = input.split(maxSplits: 1000, omittingEmptySubsequences: false) { elem -> Bool in
    if isSep {
        isSep = false
        byteCounter.0 = 0
        byteCounter.1 = elem
        return true
    }
    byteCounter.0 += 1
    if byteCounter.0 == byteCounter.1 {
        isSep = true // next is separator
    }
    return false
}.map { $0 }
请注意,
MutableRandomAccessSlice
应该是可“普通”转换为
数据的(
Data
本身是一个
MutableCollection
字节),例如,将其与简单映射进行比较,以将
ArraySlice
转换为
Array

让foo=[1,2,3]

让bar=foo[0..最后,如评论中所述,我使用了一个
DataStream
类,包括MartinR的建议。下面是我今天使用的实现

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? {
        guard self.atEnd.NOT else { return nil }
        let byte = self.data[self.index]
        self.index += 1
        return byte
    }

    func next(_ count:Int) -> Data? {
        guard self.index + count <= self.data.count else { return nil }
        let subdata = self.data.subdata(in: self.index..<self.index + count)
        self.index += count
        return subdata
    }

    func upTo(_ marker:UInt8) -> Data? {
        if let end = (self.index..<self.data.count).index( where: { self.data[$0] == marker } ) {
            let upTo = self.next(end - self.index)
            self.skip() // consume the marker
            return upTo
        }
        else {
            return nil
        }
    }

    func skip(_ count:Int = 1) {
        self.index += count
    }

    func skipThrough(_ marker:UInt8) {
        if let end = (self.index..<self.data.count).index( where: { self.data[$0] == marker } ) {
            self.index = end + 1
        }
        else {
            self.index = self.data.count
        }
    }
}
类数据流{
让数据:数据
var指数=0
var atEnd:Bool{
返回索引>=self.data.count
}
init(数据:数据){
self.data=数据
}
func next()->UInt8{
保护self.atEnd.notelse{return nil}
让byte=self.data[self.index]
自索引+=1
返回字节
}
func next(u计数:Int)->数据{

守卫self.index+count for#2你可以使用别名类型我可以看出你的沮丧,但Xcode 8仍处于测试阶段。Swift 2在Xcode 7或beta 4之前不是超级稳定的5@CodeDifferent我不确定这与xcode8或swift3有多大关系,除了假定的方向是优先使用
数据
,而不是
[UInt8]
s继续前进。我感到沮丧的一个主要原因是为什么迭代器作为值类型结构而不是引用类型对象更好。另一个选择是从数据创建一个对象,然后使用streams read方法。-但是您的数据流也是一个很好的方法。我会更改一些细节,比如检查是否足够ent数据可用,否则返回
nil
。希望您的评论是以@MartinR;回答形式给出的;带有您建议的DataStream是我最终使用的解决方案,我对此很满意。
func split(maxSplits: Int = default, omittingEmptySubsequences: Bool = default, 
           isSeparator: @noescape UInt8 throws -> Bool) 
           rethrows -> [MutableRandomAccessSlice<Data>]
/* Swift 2.2 example */
let input: [UInt8] = [2, 0x20, 0x21, 3, 0x30, 0x31, 0x32, 1, 0x10, 4, 0x40, 0x41, 0x42, 0x43]

var isSep = true
var byteCounter: (UInt8, UInt8) = (0,0)
let parsed = input.split() { elem -> Bool in
    if isSep {
        isSep = false
        byteCounter.0 = 0
        byteCounter.1 = elem
        return true
    }

    byteCounter.0 += 1
    if byteCounter.0 == byteCounter.1 {
        isSep = true // next is separator
    }
    return false

}.map { Array($0) }

print(parsed) // [[32, 33], [48, 49, 50], [16], [64, 65, 66, 67]]
let input = Data(bytes: [2, 0x20, 0x21, 3, 0x30, 0x31, 0x32, 1, 0x10, 4, 0x40, 0x41, 0x42, 0x43])

var isSep = true
var byteCounter: (UInt8, UInt8) = (0,0)
let parsed = input.split(maxSplits: 1000, omittingEmptySubsequences: false) { elem -> Bool in
    if isSep {
        isSep = false
        byteCounter.0 = 0
        byteCounter.1 = elem
        return true
    }
    byteCounter.0 += 1
    if byteCounter.0 == byteCounter.1 {
        isSep = true // next is separator
    }
    return false
}.map { $0 }
let foo = [1, 2, 3]
let bar = foo[0..<2]     // ArraySlice<Int>
let baz = bar.map { $0 } // Array<Int>
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? {
        guard self.atEnd.NOT else { return nil }
        let byte = self.data[self.index]
        self.index += 1
        return byte
    }

    func next(_ count:Int) -> Data? {
        guard self.index + count <= self.data.count else { return nil }
        let subdata = self.data.subdata(in: self.index..<self.index + count)
        self.index += count
        return subdata
    }

    func upTo(_ marker:UInt8) -> Data? {
        if let end = (self.index..<self.data.count).index( where: { self.data[$0] == marker } ) {
            let upTo = self.next(end - self.index)
            self.skip() // consume the marker
            return upTo
        }
        else {
            return nil
        }
    }

    func skip(_ count:Int = 1) {
        self.index += count
    }

    func skipThrough(_ marker:UInt8) {
        if let end = (self.index..<self.data.count).index( where: { self.data[$0] == marker } ) {
            self.index = end + 1
        }
        else {
            self.index = self.data.count
        }
    }
}