我是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是)。
答案 0 :(得分:2)
header = parse_header
仅给解析器起一个新名称。您需要一个运行分析器的功能there's one here(为简单起见,在这里选择runGet
,但您最好选择另一个功能,以便更轻松地处理错误情况):< / p>
runGet :: Get a -> ByteString -> a
请注意,它需要一个 lazy ByteString
(Data.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"