我正在尝试读取二进制文件并使用“二进制”包懒惰地解析它。包文档提供了一个示例,说明如何在不强制所有输入的情况下执行此操作,与我的场景非常相似:
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
将严格字节串重新转换回惰性字节串.我可以获得教程中描述的行为,还是二进制文件现在不支持?如果是后者,最好的方法是什么?我有一点使用管道的经验,但我不清楚如何在这里使用它。
最佳答案
您可以使用 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
我拼凑起来: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
在解码器和打印机之间,它只会读取处理请求数量的交易所需的输入: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
关于parsing - Haskell:用二进制懒惰地读取二进制文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19862583/