在为大型(<bloblength><blob>)*
编码二进制文件编写反序列化器时,我遇到了各种 Haskell 生产-转换-消费库。到目前为止,我知道四个流媒体库:
- Data.Conduit:广泛使用,有非常细心的资源管理
- 管道:类似于
conduit
(Haskell Cast #6conduit
很好地揭示了和之间的区别pipes
) - Data.Binary.Get:提供有用的功能,例如 getWord32be,但流式传输的示例很尴尬
- System.IO.Streams:似乎是最容易使用的一种
这是一个精简的示例,说明当我尝试Word32
使用conduit
. 一个更现实的例子是首先读取Word32
确定 blob 长度的 a,然后产生ByteString
该长度的惰性值(然后进一步反序列化)。但在这里我只是尝试从二进制文件中以流方式提取 Word32:
module Main where
-- build-depends: bytestring, conduit, conduit-extra, resourcet, binary
import Control.Monad.Trans.Resource (MonadResource, runResourceT)
import qualified Data.Binary.Get as G
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import qualified Data.ByteString.Lazy as BL
import Data.Conduit
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import Data.Word (Word32)
import System.Environment (getArgs)
-- gets a Word32 from a ByteString.
getWord32 :: C.ByteString -> Word32
getWord32 bs = do
G.runGet G.getWord32be $ BL.fromStrict bs
-- should read BytesString and return Word32
transform :: (Monad m, MonadResource m) => Conduit BS.ByteString m Word32
transform = do
mbs <- await
case mbs of
Just bs -> do
case C.null bs of
False -> do
yield $ getWord32 bs
leftover $ BS.drop 4 bs
transform
True -> return ()
Nothing -> return ()
main :: IO ()
main = do
filename <- fmap (!!0) getArgs -- should check length getArgs
result <- runResourceT $ (CB.sourceFile filename) $$ transform =$ CL.consume
print $ length result -- is always 8188 for files larger than 32752 bytes
程序的输出只是读取的 Word32 的数量。事实证明,流在读取第一个块(大约 32KiB)后终止。由于某种原因mbs
是 never Nothing
,所以我必须检查null bs
当块被消耗时哪个停止流。很明显,我的导管transform
有问题。我看到了解决方案的两条途径:
await
不想去 的第二块,ByteStream
那么还有另一个函数可以拉下一个块吗?在我见过的示例中(例如 Conduit 101),这不是它的完成方式- 这只是错误的设置方式
transform
。
这是如何正确完成的?这是正确的方法吗?(性能确实很重要。)
更新:这是一种不好的方法Systems.IO.Streams
:
module Main where
import Data.Word (Word32)
import System.Environment (getArgs)
import System.IO (IOMode (ReadMode), openFile)
import qualified System.IO.Streams as S
import System.IO.Streams.Binary (binaryInputStream)
import System.IO.Streams.List (outputToList)
main :: IO ()
main = do
filename : _ <- getArgs
h <- openFile filename ReadMode
s <- S.handleToInputStream h
i <- binaryInputStream s :: IO (S.InputStream Word32)
r <- outputToList $ S.connect i
print $ last r
'Bad' 表示:对时间和空间要求很高,不处理 Decode 异常。