2011-03-01 43 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]