Как преобразовать ByteString в Int и справиться с порядком байтов?

Мне нужно прочитать двоичный формат в Haskell. Формат довольно прост: четыре октета указывают длину данных, за которыми следуют сами данные. Четыре октета представляют целое число в сетевом порядке байтов.

Как я могу преобразовать ByteString из четырех байтов в целое число? Я хочу прямое приведение (в C это будет *(int*)&data), а не лексикографическое преобразование. Кроме того, как бы я поступил с порядком байтов? Сериализованное целое число находится в сетевом порядке байтов, но машина может использовать другой порядок байтов.

Я пробовал гуглить, но это были только результаты о лексикографическом преобразовании.


person Community    schedule 15.01.2013    source источник


Ответы (3)


Двоичный пакет содержит инструменты для получения целочисленных типов различных размеров и порядков байтов из 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
person Sarah    schedule 15.01.2013

Я предполагаю, что вы можете использовать складку, а затем использовать либо foldl, либо foldr, чтобы определить, какой порядок байтов вы хотите (я забыл, какой именно).

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

Я думаю, что это будет работать для бинарного оператора:

foo :: Int -> Word8 -> Int
foo prev v = (prev * 256) + v
person Pubby    schedule 15.01.2013

Я бы просто извлек первые четыре байта и объединил их в одно 32-битное целое число, используя функции в 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
person Frerich Raabe    schedule 15.01.2013