4

我正在尝试读取二进制文件并使用“二进制”包懒惰地解析它。包文档提供了一个示例,说明如何在不强制输入与我的非常相似的场景的所有输入的情况下执行此操作:

 example2 :: BL.ByteString -> [Trade]
 example2 input
  | BL.null input = []
  | otherwise =
    let (trade, rest, _) = runGetState getTrade input 0
    in trade : example2 rest

但是,这使用了已弃用的runGetState功能,该功能本身将您指向runGetIncremental函数。

问题是“runGetIncremental”函数似乎强制剩余的输入是严格的字节串,从而强制它将整个文件加载到内存中。事实上,当我尝试运行它时,我看到大约 6GB 的内存使用量。甚至runGetStatenow 的实现似乎也是基于runGetIncremental然后使用chunk.

我可以得到教程中描述的行为,还是现在二进制不支持?如果是后者,最好的方法是什么?我有一点使用导管的经验,但我不清楚如何在这里使用它。

4

1 回答 1

2

您可以使用pipes-binary和来做到这一点pipes-bytestring。这是一个帮助您的功能:

import Control.Monad (void)
import Data.Binary
import Pipes
import Pipes.Binary (decodeMany)
import Pipes.ByteString (fromHandle)
import qualified Pipes.Prelude as P
import System.IO

decodeHandle :: (Binary a) => Handle -> Producer a IO ()
decodeHandle handle = void $ decodeMany (fromHandle handle) >-> P.map snd

voidand之所以存在,map snd是因为decodeMany实际上返回了更多信息(如字节偏移和解析错误)。如果您确实想要该信息,则只需删除它们即可。

这是一个如何使用的示例,使用我拼凑decodeHandle的快速骨架:Trade

data Trade = Trade

instance Binary Trade where
    get   = return Trade
    put _ = return ()

instance Show Trade where show _ = "Trade"

main = withFile "inFile.txt" ReadMode $ \handle -> runEffect $
    for (decodeHandle handle) $ \trade -> do
        lift $ print (trade :: Trade)
        -- do more with the parsed trade

您可以使用for循环解码的交易并处理它们,或者如果您愿意,您可以使用管道组合:

main = withFile "inFile.txt" ReadMode $ \handle -> runEffect $
    decodeHandle handle >-> P.print

这将是懒惰的,并且只解码您实际需要的交易数量。因此,如果您take在解码器和打印机之间插入 a,它只会读取处理请求的交易数量所需的输入:

main = withFile "inFile.txt" ReadMode $ \handle -> runEffect $
    for (decodeHandle handle >-> P.take 4) $ \trade -> do
        ... -- This will only process the first 4 trades

-- or using purely pipe composition:

main = withFile "inFile.txt" ReadMode $ \handle -> runEffect $
    decodeHandle handle >-> P.take 4 >-> P.print
于 2013-11-08T17:55:41.727 回答