2011-03-01 14 views
8

estoy leyendo en una estructura de un archivo binario que contiene números enteros de 16 bits firmados utilizando el Obtener mónada de Data.Binary. Mi código actual se parece a:Manipulación de datos binario con signo en Haskell sin unsafeCoerce

data DetectorStats = DetectorStats Int16 Word8 Word8 
        Word8 Int16 Version Int16 
        deriving Show 

getDetectorStats :: Get DetectorStats 
getDetectorStats = do 
    productNumber <- getWord16be 
    bitPerCoordinate <- getWord8 
    energyCapability <- getWord8 
    timingCapability <- getWord8 
    clockFrequency <- getWord16be 
    serialNumber <- getWord16be 
    return (DetectorStats (unsafeCoerce productNumber) 
         bitPerCoordinate 
         energyCapability 
         timingCapability 
         (unsafeCoerce clockFrequency) 
         firmwareVersion 
         (unsafeCoerce serialNumber)) 

No estoy feliz por el uso de unsafeCoerce, pero no parece ser una manera de leer en un Int16 directamente, ni una forma de convertir el Word16 en un Int16. ¿Hay una mejor manera de manejar esto?

Respuesta

8

fromIntegral convertirá Word16 a Int16. Sin embargo, debe verificar que obtenga el resultado que anticipa con respecto a la firma.

+1

Me parece bien: 'fromIntegral (maxBound - 4 :: Word16) :: Int16' .. '> -5'. – sclv

+3

El documento dice: "fromIntegral convierte tipos WOARD a Byte por representación, no por valor" – fuz

3

El paquete Data.Convertible debe hacer lo que está pidiendo.

Por ejemplo, para convertir de Word16 a Int16:

> (convert (6 :: Word16)) :: Int16 
6 
+1

El problema es que eso no funciona en números negativos > (convertir (-1 :: Word16)) :: Int16 *** Excepción: Convertible: error al convertir los datos fuente 65535 del tipo Word16 al tipo Int16: valor de entrada fuera de los límites: (-32768,32767) – user640078

1

Basándose en la respuesta de Stephen aquí hay una implementación de las funciones get y put para Int8, Int16 e Int32 análogas a las existentes para Word8, Word16 y Word32. No he todavía requiere Int64 o soporte-Host endian pero estos se pueden añadir:

{-# LANGUAGE RecordWildCards #-} 
module GetAndPutForInt 
(getInt8 
, getInt16be 
, getInt16le 
, getInt32be 
, getInt32le 
, putInt8 
, putInt16be 
, putInt16le 
, putInt32be 
, putInt32le 
) where 

import Data.Binary 
import Data.Binary.Get 
import Data.Binary.Put 

import Data.Int 
import Data.Word 

import qualified Data.ByteString.Lazy as B 

getInt8 :: Get Int8 
getInt8 = do a <- getWord8 
      return $ fromIntegral a 
getInt16be :: Get Int16 
getInt16be = do a <- getWord16be 
       return $ fromIntegral a 
getInt16le :: Get Int16 
getInt16le = do a <- getWord16le 
       return $ fromIntegral a 
getInt32be :: Get Int32 
getInt32be = do a <- getWord32be 
       return $ fromIntegral a 
getInt32le :: Get Int32 
getInt32le = do a <- getWord32le 
       return $ fromIntegral a 

putInt8 :: Int8 -> Put 
putInt8 i = putWord8 ((fromIntegral i) :: Word8) 
putInt16be :: Int16 -> Put 
putInt16be i = putWord16be ((fromIntegral i) :: Word16) 
putInt16le :: Int16 -> Put 
putInt16le i = putWord16le ((fromIntegral i) :: Word16) 
putInt32be :: Int32 -> Put 
putInt32be i = putWord32be ((fromIntegral i) :: Word32) 
putInt32le :: Int32 -> Put 
putInt32le i = putWord32le ((fromIntegral i) :: Word32) 

data TestType = TestType 
    { a :: Int16 
    , b :: Int16 
    } deriving (Show, Eq) 

instance Binary TestType where 
    put TestType{..} = 
     do putInt16be a 
     putInt16le b 
    get = do a <- getInt16be 
      b <- getInt16le 
      return TestType{..} 

main :: IO() 
main = do 
    putStrLn "Supplies Get and Put support to Int8, Int16 etc. types as Data.Binary.Get and Data.Binary.Push do for Word8, Word 16 etc." 
    putStrLn "" 
    putStrLn "Test data in bytes:" 
    print bytes 
    putStrLn "" 
    putStrLn "As TestType:" 
    print (decode bytes :: TestType) 
    putStrLn "" 
    putStrLn "Back to bytes:" 
    print $ (encode ((decode bytes) :: TestType)) 
    where 
    bytes = B.pack $ concat $ replicate 2 [0xCD,0xEF] 
Cuestiones relacionadas