Reputation:
I'm new to Haskell, so I'm probably missing something very simple.
I'm having a supprisingly hard time trying to parse a structured binary stream.
The binary stream has varying and conditional parts (like a field which determines how many items follow it (ncount
), or what type of message follows it (type
)).
To get a simple working example, I'm trying to parse this hypothetical binary structure:
+----------------+---------------+--------------
| 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) |
+----------------+---------------+
...
My code so far:
{-# 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"
In which I get the error:
* No instance for (Show (Get Header)) arising from a use of `print'
* In the expression: print header
Obviously, I plan to parse this recursively, but for now I can't even see a value I've read.
I've followed https://wiki.haskell.org/Dealing_with_binary_data but this uses Data.Binary.Strict (binary-strict) which doesn't compile on Windows (atleast on mine).
I've also followed https://hackage.haskell.org/package/binary-bits-0.5/docs/Data-Binary-Bits-Get.html but it doesn't show how to use values you have gotten with getWord8
(Do I need to put
them into an Int to read them as decimal?)
Again, I'm new to Haskell and not familiar with Monads (which I believe Get is).
Upvotes: 1
Views: 476
Reputation: 33389
header = parse_header
is only giving a new name to the parser. You need a function to run the parser, there's one here (here choosing runGet
for simplicity, but you should prefer the other one, to handle the error case more easily):
runGet :: Get a -> ByteString -> a
Note that it takes a lazy ByteString
(Data.ByteString.Lazy
) instead of a strict one (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"
Upvotes: 2