2011-03-01 44 views
8

我在一个结构从包含Data.Binary使用获取单子签署16位整数的二进制文件中读取符号二进制数据。我当前的代码看起来像:处理哈斯克尔没有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)) 

我并不喜欢使用unsafeCoerce,但不会出现在一个的Int16直接读取的方式,也没有一种方法来转换Word16纳入Int16。有没有更好的方法来处理这个问题?

回答

8

fromIntegral将WORD16转换为Int16的。但是,您必须检查它是否获得了您预期的与签署相关的结果。

+1

对我很好:'fromIntegral(maxBound - 4 :: Word16):: Int16'''> -5'。 – sclv 2011-03-01 22:34:42

+3

该文档说:“从一体转换WOrd到字节类型的代表,而不是价值” – fuz 2011-03-02 12:48:48

3

Data.Convertible包应该做你要求的。

例如转换从Word16Int16

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

的问题是,失败的负数 >(转换(-1 :: WORD16)):: Int16的 ***例外:可转换:将Word16类型的源数据65535错误转换为Int16类型:输入值超出范围:(-32768,32767) – user640078 2011-03-01 22:20:10

1

基于Stephen的答案建立在这里是一个实现Int8,Int16和Int32的获取和放置函数,类似于现有的Word8,Word16和Word32。我至今还未要求Int64或主机端的支持,但这些可以添加:

{-# 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]