5

假设我有以下类型:

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

有没有办法将所有这些全部转储到一个 ByteString (或类似的结构)中(以 ye olde C 结构的方式)?如果没有,我必须编写一个函数,将它们分别放入一个列表中,是否至少有一些函数可以使将值粘贴到 Word8 列表中变得容易?像 putWordBBxe 之类的东西,除了字节字符串或列表(尽管我很可能严重错误,因为我还没有正确读入 Monads,在我看来 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

镜头就像单个字节组的安全解释器。但是,存在三个问题:首先,您应该为此使用一个测试框架,因为很容易弄错镜头。其次,每次更改都需要 ByteString 的新副本。根据您的操作,这可能比您原来的方法更慢或更快。

我个人的建议是使用常规的高级 Haskell 数据类型并使用适当的序列化。正如其他人指出的那样,这些实例很容易编写。

于 2013-02-17T14:12:16.817 回答