鉴于一个png文件,我试图获取其块的列表'偏移量和尺寸。
简而言之,png files由块组成,每个块由三个4字节字段和一个可变长度字段(块的数据字段)组成。数据字段的大小存储在第一个4字节字段中(称为"长度"字段)。
因此,给定当前块的偏移量和大小(ofs,sz),可以得到下一个块的偏移量和大小(ofs',sz'),这样:
OFS' = ofs + sz
阅读sz'在offset = ofs'
给定png文件中的初始块的偏移量和大小,总是(0,8),可以循环遍历文件,直到达到它的结尾。这就是我如何做到的:
import Data.Word
import qualified Data.ByteString.Lazy as BS
import Data.Binary.Get
size :: BS.ByteString -> Int -> IO (Int)
size bytes offset = do
let ln = runGet (do skip offset
l <- getWord32be
return l)
bytes
return $ 3*4 + fromIntegral ln
offsetSizes :: Int -> BS.ByteString -> [(Int, Int)] -> IO [(Int, Int)]
offsetSizes fLen bytes oss = do
let (offset, sz) = last oss
offset' = offset + sz
sz' <- size bytes offset'
let nextOffset = offset' + sz'
if nextOffset < fLen then offsetSizes fLen bytes $ oss ++ [(offset', sz')]
else return oss
main = do
contents <- BS.readFile "myfile.png"
let fLen = fromIntegral $ BS.length contents :: Int
ofszs <- offsetSizes fLen contents [(0,8)]
putStrLn $ "# of chunks: " ++ (show $ length ofszs)
putStrLn $ "chunks [(offset,size)]: " ++ show ofszs
我的问题:我对循环并不满意。我想知道在Haskell中是否有更惯用的方法来实现相同的目标?
答案 0 :(得分:1)
offsetSizes
重复输入一些状态((offset, sz)
对)以产生新的一对或完成。创建的所有对都被收集到一个列表中。
这个递归方案由unfoldrM
包中的monad-loops
捕获,允许您将offsetSizes
写为
offsetSizes :: Int -> BS.ByteString -> IO [(Int, Int)]
offsetSizes fLen bytes = unfoldrM step (0, 8)
where
step (offset, sz) = do
let offset' = offset + sz
sz' <- size bytes offset'
let state' = (offset', sz')
return $ if offset' + sz' < fLen then Just (state', state') else Nothing