在haskell中编写结构化二进制文件

时间:2014-04-26 13:27:09

标签: haskell binaryfiles

我想在Haskell中编写一个结构化的二进制文件。 例如,假设前四个字节应为“TEST”(以ASCII为单位),后跟数字1,2,3,4,然后是32个字节,每个字节值为128,然后是Little Endian格式的数字2048。 / p>

这意味着,创建的文件(如十六进制)应如下所示:

  

54 45 53 54 01 02 03 04 80 80 [... 30字节以上...] 00 08

所以基本上我有一个自定义数据结构,比如说

data MyData = MyData {
  header :: String     -- "TEST"
  n1     :: Integer    -- 1
  n2     :: Integer    -- 2
  n3     :: Integer    -- 3
  block  :: [Integer]  -- 32 times 128
  offset :: Integer    -- 2048
}

现在我想将此数据写入文件。所以基本上我需要将这个结构转换成一个长ByteString。我找不到干净的惯用方法来做到这一点。理想情况下我有一个功能

MyDataToByteString :: MyData -> ByteString

MyDataToPut :: MyData -> Put

但我无法找到如何创建这样的功能。

背景资料:我想以脉冲跟踪器格式(http://schismtracker.org/wiki/ITTECH.TXT)编写一首歌,这是分裂跟踪器软件的二进制格式。

更新1

对于endian转换,我想我可以按如下方式提取单个字节:

getByte :: Int -> Int -> Int
getByte b num = shift (num .&. bitMask b) (8-8*b)
  where bitMask b = sum $ map (2^) [8*b-8 .. 8*b-1]

2 个答案:

答案 0 :(得分:2)

你可以尝试这个(恐怕不是最佳的):

import qualified Data.List as L
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString as B

data MyData = MyData {
      header :: String     -- "TEST"
    , n1     :: Integer    -- 1
    , n2     :: Integer    -- 2
    , n3     :: Integer    -- 3
    , n4     :: Integer    -- 4
    , block  :: [Integer]  -- 32 times 128
    , offset :: [Integer]  -- 2048 in little endian
} deriving (Show)

d = MyData "TEST" 1 2 3 4 (L.replicate 32 128) [0, 8]

myDataToByteString :: MyData -> B.ByteString
myDataToByteString (MyData h n1 n2 n3 n4 b o) =
    B.concat [
          BC.pack h
        , B.pack (map fromIntegral [n1, n2, n3, n4])
        , B.pack (map fromIntegral b)
        , B.pack (map fromIntegral o)
    ]

答案 1 :(得分:2)

binarycereal包提供了数据结构的简单ByteString序列化。以下是使用binary

的示例
{-# LANGUAGE DeriveGeneric #-} 

import Data.Binary
import qualified Data.ByteString.Char8 as B8
import Data.Word8                  
import GHC.Generics

data MyData = MyData                  
    { header :: B8.ByteString               
    , n1     :: Int                 
    , n2     :: Int                  
    , n3     :: Int                 
    , n4     :: Int                 
    , block  :: [Word8]               
    , offset :: Int                 
    } deriving (Generic, Show)

instance Binary MyData

myData = MyData (B8.pack "Test") 1 2 3 4 (replicate 32 128) 2048

注意我已将block的类型更改为[Word8],因为问题表明这些是字节

我们为GHC.Generics派生MyData个实例,允许binary包自动生成Binary个实例。现在,我们可以使用ByteString将数据序列化为encode,并使用decode反序列化:

λ. let encoded = encode myData
λ. :t encoded
encoded :: Data.ByteString.Lazy.Internal.ByteString
λ. let decoded = decode encoded :: MyData
λ. decoded
MyData {header = "Test", n1 = 1, n2 = 2, n3 = 3, n4 = 4, block = [128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128], offset = 2048}