5

私が次のタイプを持っているとしましょう:

data WaveFormatChunk = WaveFormatChunk { 
    compression :: Word16,
    channels :: Word16,
    sampleRate :: Word32,
    averageBps :: Word32,
    blockAlign :: Word16,
    significantBits :: Word16
    } deriving (Show)

そのすべてを ByteString (または同様の構造) ホールセールに (古い C 構造体のように) ダンプする方法はありますか? そうでない場合、それらすべてを個別にリストに入れる関数を作成する必要がある場合、Word8リストなどに値を簡単に貼り付ける関数は少なくともありますか? バイト文字列またはリストを除いて、putWordBBxe のようなもの (モナドをまだ適切に読み込んでいないため、おそらく重大な誤解があると思われますが、Get/Put は主にストリームで使用されるようです)。

Data.Binary は私が探しているものではありません。特定の (そして「間違った」) エンディアンを持つ特定の形式でデータを保存するよりも、データをディスクにダンプする方が便利なようです。

4

2 に答える 2

12

Data.Binaryを使用すると、明示的にリトル エンディアンの演算子を使用して、構造をバイト文字列にシリアル化できます。

{-# OPTIONS_GHC -funbox-strict-fields #-}
{-# LANGUAGE RecordWildCards #-}

import Data.Binary
import Data.Binary.Put

import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy  as L

data WaveFormatChunk =
        WaveFormatChunk { 
            compression     :: !Word16,
            channels        :: !Word16,
            sampleRate      :: !Word32,
            averageBps      :: !Word32,
            blockAlign      :: !Word16,
            significantBits :: !Word16
        } 

instance Binary WaveFormatChunk where
    put (WaveFormatChunk{..}) = do
        putWord16le compression 
        putWord16le channels
        putWord32le sampleRate
        putWord32le averageBps
        putWord16le blockAlign
        putWord16le significantBits

    get = undefined

main = C.putStr $ C.concat $ L.toChunks $ encode test
  where
    test = WaveFormatChunk {
            compression     = 0xcafe
          , channels        = 0xface
          , sampleRate      = 0xdeadbeef
          , averageBps      = 0xf01dab1e
          , blockAlign      = 0x5566
          , significantBits = 0xb01d
          }

生成されます:

 $ ./A | od -x
 0000000 cafe face beef dead ab1e f01d 5566 b01d

したがって、表現を正確にバイトレベルで制御できます。ストリーミングに興味がない場合は、シリアル パッケージからも同じ効果を得ることができます。

于 2013-02-17T12:46:44.063 に答える
4

別の、まったく異なるアプローチがあります。このような構造を持つ代わりに、ByteStringラッパーを定義できます。

import Data.ByteString (ByteString)

newtype WaveFormatChunk =
    WaveFormatChunk {
      getWaveFormatChunk :: ByteString
    }

これをファイルに書き込むのは簡単です。このような構造を変更するには、レンズを使用できます。

data Compression = {- ... -}

compression :: Lens' WaveFormatChunk Compression

または、必要に応じて:

compression :: Lens' WaveFormatChunk Word16

レンズは、個々のバイト グループの安全なインタープリターのように機能します。ただし、3 つの問題があります。まず、レンズを間違えやすいため、そのフレームワーク用のテスト フレームワークを使用する必要があります。第 2 に、すべての変更には ByteString の新しいコピーが必要です。何をするかによって、これは元のアプローチよりも遅くなったり速くなったりする可能性があります。

私の個人的な推奨事項は、通常の高レベルの Haskell データ型を使用し、適切なシリアル化を使用することです。他の人が指摘したように、インスタンスは非常に簡単に記述できます。

于 2013-02-17T14:12:16.817 に答える