【问题标题】:Idiomatic method of parsing swift(3) data streams解析swift(3)数据流的惯用方法
【发布时间】: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..&lt;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


【解决方案1】:

最后,正如 cmets 中所述,我参加了 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
        }
    }
}

【讨论】:

    【解决方案2】:

    (免责声明:我在再次阅读 OP:s 问题时意识到(不仅仅是使用 TL;DR 眼睛;)这并不能真正回答 OP:s 问题,但我将其保留为它可能会为讨论增加一些有趣的内容;split 此处不生成流,而是生成数据序列块)

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

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

    按顺序返回序列中可能最长的子序列, 不包含满足给定谓词的元素。元素 用于拆分序列的不会作为任何的一部分返回 子序列。

    来自The Language Reference for the Data structure (Swift 3)

    我自己没有时间下载 XCode 8 测试版(并且 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 结构的未经测试的等效 sn-p:

    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&lt;Data&gt; 应该“微不足道”地转换为 DataData 本身是一个 MutableCollection 字节),比较例如到 self 上的简单映射将 ArraySlice&lt;Int&gt; 转换为 Array&lt;Int&gt;

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-23
      • 1970-01-01
      • 1970-01-01
      • 2017-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多