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

8yoxcaq7  于 2023-08-02  发布在  Swift
关注(0)|答案(2)|浏览(85)

我正在尝试对Swift 3Data对象进行一些简单的BSON解析。我觉得我在和体制抗争。
让我们从一些输入和方案开始:

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

字符串
这仅仅是一个简单的数据流,一个无关紧要的方案是,前导字节指示后面有多少字节构成下一个块。因此,在上文中,前导2指示0x 20、0x 21是第一个块,随后是包含字节0x 30、0x 31、0x 32等的3字节块。

溪流

我的第一个想法是用流来做(呃,Generator,Iterator,随便什么)。所以我最后得到了这样的结果:

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放在我所有的解析函数上。
2)我觉得我用IndexingIterator过度指定了参数类型。也许我只是需要习惯于冗长的泛型?

Python结构

我对这种方法感到沮丧,我想我可以模仿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方法只采用开放范围。这是怎么回事

开放式反抗旧习惯踢

现在我很恼火这个老滑头似乎在这里找不到幸福,我只好自己卷了一卷:

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版本

经过这段时间,我发现自己很好奇什么是最惯用的方式来通过数据结构流,从它们中提取数据,基于在它们中遇到的内容。

vdzxcuhz

vdzxcuhz1#

最后,正如在评论中提到的,我使用了一个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
        }
    }
}

字符串

wpcxdonn

wpcxdonn2#

  • (免责声明:当我再次阅读OP:s的问题时,我意识到(不仅仅是使用TL;这并没有真正回答OP的问题,但我将离开它,因为它可能会使讨论有点有趣; split在这里不产生流,而是一个数据序列块)*

这有点类似于你的 “Python Struct'esque” 解决方案;你可以使用Datasplit方法的isSeparator predicate 闭包来解析你的字节流。

func split(maxSplits: Int = default, omittingEmptySubsequences: Bool = default, 
           isSeparator: @noescape UInt8 throws -> Bool) 
           rethrows -> [MutableRandomAccessSlice<Data>]

字符串
按顺序返回序列中不包含满足给定 predicate 的元素的可能最长的子序列。用于拆分序列的元素不会作为任何子序列的一部分返回。
The Language Reference for the Data structure (Swift 3)
我自己还没有时间下载XCode 8 beta版(IBM Swift 3-dec Sandbox出于某种原因不包括Data结构),但有一个使用split(...)的例子(这里:只是一个数组)可以看起来沿着这样的东西:

/* 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]]


Swift 3 Data结构的未测试等效代码片段:

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>应该“平凡地”可转换为DataData本身是MutableCollection字节),例如:将ArraySlice<Int>转换为Array<Int>

let foo = [1, 2, 3]
let bar = foo[0..<2]     // ArraySlice<Int>
let baz = bar.map { $0 } // Array<Int>

相关问题