【问题标题】:Writing structured binary files in haskell在haskell中编写结构化的二进制文件
【发布时间】:2014-04-26 13:27:09
【问题描述】:

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

也就是说,创建的文件(十六进制)应该是这样的:

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
}

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

MyDataToByteString :: MyData -> ByteString

MyDataToPut :: MyData -> Put

但我不知道如何创建这样的函数。

背景信息:我想写一首脉冲追踪​​器格式的歌曲(http://schismtracker.org/wiki/ITTECH.TXT),这是分裂追踪器软件的二进制格式。

更新 1

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

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]

【问题讨论】:

    标签: haskell binaryfiles


    【解决方案1】:

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

    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)
        ]
    

    【讨论】:

    • 这很好,但你作弊了一点 - 你手动输入了 [0, 8] 作为 2048 的小端表示。那么我如何以编程方式到达那里?
    • @Julian 哦,你可以用Data.Bits的函数来做,应该不难。
    • 感谢您的提示,我想这可以解决问题(请参阅相关更新)。
    【解决方案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],因为问题指出这些将是 字节

    我们为MyData 派生了一个GHC.Generics 实例,它允许binary 包自动生成一个Binary 实例。现在我们可以使用encode将我们的数据序列化为ByteString,并使用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}
    

    【讨论】:

    • 我无法运行您的示例。如果我在 ghci 中加载你的主文件,我会得到Not in scope: type constructor or class 'ByteString'。如果我添加import qualified Data.ByteString as B 并将ByteString 更改为B.ByteString,我会得到Couldn't match expected type 'B.ByteString' with actual type '[Char]'
    • @Julian:我已经编辑了我的答案以包括导入和正确的 StringByteString 转换。
    • 这很有帮助 - 但是当我尝试显示或保存编码的 ByteString 时,我仍然得到一个 No instance nor default method for class operation Data.Binary.put。我错过了什么?
    • 如果您在ghci 中运行此代码,请确保您使用的是:set -XDeriveGeneric
    • 不会改变任何东西,当我使用“runhaskell”运行时也是如此。当我尝试执行BS.writeFile "test" encodedBS.putStr encoded 时会发生错误。我想我使用了错误的 ByteString 模块(我使用了import qualified Data.ByteString.Lazy as BS)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多