Haskell-解析二进制流

时间:2018-07-15 15:36:24

标签: parsing haskell binary bit

我是Haskell的新手,所以我可能缺少一些非常简单的内容。

我很难解析结构化的二进制流。 二进制流具有变化和有条件的部分(例如,一个字段,该字段确定跟随它的项数(ncount),或跟随它的消息类型(type)。

为获得一个简单的工作示例,我试图解析这种假设的二进制结构:

+----------------+---------------+--------------
| Magic (8 bits) | type (3 bits) | type message...
+----------------+---------------+--------------
Type 1:
+----------------+-------------+-------------+-----------------+
|ncount (3 bits) | n1 (3 bits) | n1 (3 bits) | nN (3 bits)...  |
+----------------+-------------+-------------+-----------------+
Type 2:
+----------------+---------------+
|  num1 (7 bits) | num2 (7 bits) |
+----------------+---------------+
...

到目前为止,我的代码:

{-# LANGUAGE RecordWildCards #-}

module Main where

import Data.Bits
import Data.Binary                      as B
import qualified Data.Binary.Bits.Get   as BG
import qualified Data.ByteString        as BS

data Header = Header {
     magic  :: Word8
    ,mtype  :: Word8
    ,num1   :: Word8
    ,num2   :: Word8
} deriving (Show)

--instance Show (Get Header) where
--    show (Header {..}) = show . magic

parse_header :: B.Get Header
parse_header = BG.runBitGet parse_header'

-- Example, assume type 2 for now
parse_header' :: BG.BitGet Header
parse_header' = do
    magic   <- BG.getWord8 8
    mtype   <- BG.getWord8 3
    num1    <- BG.getWord8 7
    num2    <- BG.getWord8 7
    return $ Header magic mtype num1 num2

main :: IO ()
main = do
    putStrLn "Start"

    -- File containing binary stream
    fstr <- BS.readFile "data/hbin.bin"

    let header = parse_header
        in 
            -- How do I print out Header?
            print header
            -- * No instance for (Show (Get Header)) 
            -- arising from a use of `print'
            -- * In the expression: print header

    putStrLn "\nEnd"

出现错误的地方:

* No instance for (Show (Get Header)) arising from a use of `print'
* In the expression: print header

很显然,我打算递归地解析它,但是现在我什至看不到我已经读过的值。

我遵循了https://wiki.haskell.org/Dealing_with_binary_data,但这使用的是Data.Binary.Strict(二进制严格),它在Windows上不会编译(至少在我的系统上)。

我也遵循了https://hackage.haskell.org/package/binary-bits-0.5/docs/Data-Binary-Bits-Get.html,但是它没有显示如何使用您在getWord8中获得的值(我是否需要将put插入Int才能读取为小数点?)

再次,我是Haskell的新手,对Monads不熟悉(我相信Get是)。

1 个答案:

答案 0 :(得分:2)

header = parse_header仅给解析器起一个新名称。您需要一个运行分析器的功能there's one here(为简单起见,在这里选择runGet,但您最好选择另一个功能,以便更轻松地处理错误情况):< / p>

runGet :: Get a -> ByteString -> a

请注意,它需要一个 lazy ByteStringData.ByteString.Lazy)而不是一个 strict 一个(Data.ByteString)。

...
import Data.ByteString.Lazy (toLazy)

...

main = do
  fstr <- BS.readFile "data/hbin.bin"
  let header = runGet parse_header (fromStrict fstr)
  print header
  putStrLn "End"