How to convert a ByteString to an Int and dealing with endianness?

谁说我不能喝 提交于 2019-12-23 10:15:57

问题


I need to read a binary format in Haskell. The format is fairly simple: four octets indicating the length of the data, followed by the data. The four octets represent an integer in network byte-order.

How can I convert a ByteString of four bytes to an integer? I want a direct cast (in C, that would be *(int*)&data), not a lexicographical conversion. Also, how would I go about endianness? The serialized integer is in network byte-order, but the machine may use a different byte-order.

I tried Googling but that only yold results about lexicographical conversion.


回答1:


The binary package contains tools to get integer types of various sizes and endianness from ByteStrings.

λ> :set -XOverloadedStrings
λ> import qualified Data.Binary.Get as B
λ> B.runGet B.getWord32be "\STX\SOH\SOH\SOH"
33620225
λ> B.runGet B.getWord32be "\STX\SOH\SOH\SOHtrailing characters are ignored"
33620225
λ> B.runGet B.getWord32be "\STX\SOH\SOH" -- remember to use `catch`:
*** Exception: Data.Binary.Get.runGet at position 0: not enough bytes
CallStack (from HasCallStack):
  error, called at libraries/binary/src/Data/Binary/Get.hs:351:5 in binary-0.8.5.1:Data.Binary.Get



回答2:


I assume you can use a fold, and then use either foldl or foldr to determine which endian you want (I forget which is which).

foldl :: (a -> Word8 -> a) -> a -> ByteString -> a

I think this will work for the binary operator:

foo :: Int -> Word8 -> Int
foo prev v = (prev * 256) + v



回答3:


I'd just extract the first four bytes and merge them into a single 32bit integer using the functions in Data.Bits:

import qualified Data.ByteString.Char8 as B
import Data.Char (chr, ord)
import Data.Bits (shift, (.|.))
import Data.Int (Int32)

readInt :: B.ByteString -> Int32
readInt bs = (byte 0 `shift` 24)
             .|. (byte 1 `shift` 16)
             .|. (byte 2 `shift` 8)
             .|. byte 3
        where byte n = fromIntegral $ ord (bs `B.index` n)

sample = B.pack $ map chr [0x01, 0x02, 0x03, 0x04]
main = print $ readInt sample -- prints 16909060


来源:https://stackoverflow.com/questions/14335725/how-to-convert-a-bytestring-to-an-int-and-dealing-with-endianness

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!