我正在尝试读取二进制文件并使用’binary’包来懒散地解析它.包文档提供了一个如何执行此操作的示例,而不强制与我非常类似的场景的所有输入:
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.现在,即使runGetState的实现似乎也基于runGetIncremental,然后使用chunk将strict字符串重新转换回惰性字符串.
我可以获得教程中描述的行为,还是二进制文件现在不支持?如果是后者,那么最好的方法是什么?我有一点使用管道的经验,但我不清楚如何在这里使用它.
最佳答案 您可以使用
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
void和map snd在那里,因为decodeMany实际上返回更多信息(如字节偏移和解析错误).如果您确实需要该信息,那么只需删除它们即可.
下面是一个如何使用decodeHandle的示例,使用快速骨架进行Trade I汇总:
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
这将是懒惰的,只能解码您实际需要的交易数量.因此,如果您在解码器和打印机之间插入一个接收器,它将只读取所需的输入以处理所请求的交易数量:
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