2016-06-27 12 views
2

Swift3 Dataオブジェクトの簡単なBSON解析をしようとしています。私はシステムと戦っているように感じる。swift(3)データストリームの構文解析の慣用方法

いくつかの入力およびスキームで始まるのをしてみましょう:

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バイトのチャンクに続く最初のチャンクであることを示している私の最初の考えである

ストリームストリーム(er、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イテレータをしませんか?このことの全体的なポイントは、コレクション上の進化する位置を追跡することです。スウィフトの作者がイテレータを「すべての恩恵セマンティクス」のもとに送ることを選んだのは本当に苦労しています。つまり、すべての解析関数にinoutを入れなければならないということです。

2)IndexingIteratorで引数の型を指定しすぎているような気がします。たぶん、冗長なジェネリックに慣れる必要があるのでしょうか?そのアプローチに不満のPython Struct'esque

は、私がニシキヘビ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 

私はこれに2つの問題がありました。

1)本当に返信したいのは、data[1..count], data.subdata(in: count+1..<data.count)です。しかし、これはMutableRandomAccessSliceを返します。どちらが全く違う種類のようですか?だから私はより多くの関与を使用して終了subdata

2)1つは閉じた範囲のデータを添字にすることができますが、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を肉体化することができます。しかし、...私は今、暴かれた道を離れて、私が "それを得る"(敏速な電球)ではないように感じています。

TL; DRバージョン

これは遊んでた後、私は彼らに遭遇されたものに基づいて、それらからデータを抽出、自分は好奇心どのようなデータ構造体を通じてストリーミングするための最も慣用的な方法を見つけます。

+0

#2のためにあなたは私があなたの欲求不満を見ることができるタイプの別名 – Alexander

+0

を使用することができますが、Xcodeの8はまだベータ版です。 Swift 2は、Xcode 7 Beta 4または5までは安定していませんでした。 –

+0

@Codeこれはxcode8やswift3とは多分関係がありますが、 '[UInt8]'よりも 'Data'を優先的に使う方向次なる私の不満の主な原因は、イテレータが参照型オブジェクトの代わりに値型構造体として優れている理由です。 –

答えて

1

を変換するための自己オーバーシンプルなマップに、コメントで述べたように、私はMartinRによって提案を含めDataStreamクラスに行ってきました。ここに私が今日使っている実装があります。

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 
     } 
    } 
} 
0

(免責事項:私はOPの質問をもう一度読んで(実際のTL; DRの目だけではなく)これが実際にOPの質問に答えるわけではないことを認識していますが、 splitはここストリームを生成しないのではなく、データ列のチャンク)

これはあなたの「PythonのStruct'esque」ソリューションに多少似ている。議論にやや興味深い追加を行うかもしれません。 splitメソッドのisSeparator述語クロージャをDataにして、バイトストリームを解析することができます。

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スウィフト3 - 12月サンドボックスが何らかの理由で、 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]] 

スウィフト3 Data構造のための非テストと同等スニペット:MutableRandomAccessSlice<Data>は「自明」transformablでなければならないこと

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 } 

はノートの線に沿って何かを見ることができますeから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>