abs*_*nce 5 parsing haskell attoparsec haskell-pipes
我正在尝试使用Haskell中的pipes-attoparsec解析二进制数据.管道(代理)涉及的原因是将读取与解析交错以避免大文件的高内存使用.许多二进制格式基于块(或块),它们的大小通常由文件中的字段描述.我不确定这个块的解析器是什么被调用,但这就是我所说的标题中的"sub-parser".我遇到的问题是以简洁的方式实现它们而没有可能很大的内存占用.我想出了两个在某些方面都失败的替代方案.
备选方案1是将块读入单独的字节串并为其启动单独的解析器.简洁,大块将导致高内存使用.
备选方案2是在相同的上下文中保持解析并跟踪消耗的字节数.这种跟踪容易出错,并且似乎会影响组成最终blockParser的所有解析器.对于格式错误的输入文件,在比较跟踪大小之前,还可以通过进一步解析比大小字段所指示的方式来浪费时间.
import Control.Proxy.Attoparsec
import Control.Proxy.Trans.Either
import Data.Attoparsec as P
import Data.Attoparsec.Binary
import qualified Data.ByteString as BS
parser = do
size <- fromIntegral <$> anyWord32le
-- alternative 1 (ignore the Either for simplicity):
Right result <- parseOnly blockParser <$> P.take size
return result
-- alternative 2
(result, trackedSize) <- blockparser
when (size /= trackedSize) $ fail "size mismatch"
return result
blockParser = undefined
main = withBinaryFile "bin" ReadMode go where
go h = fmap print . runProxy . runEitherK $ session h
session h = printD <-< parserD parser <-< throwParsingErrors <-< parserInputD <-< readChunk h 128
readChunk h n () = runIdentityP go where
go = do
c <- lift $ BS.hGet h n
unless (BS.null c) $ respond c *> go
Run Code Online (Sandbox Code Playgroud)
我喜欢称其为“固定输入”解析器。
我可以告诉你怎么pipes-parse做。pipes-parse您可以在库的功能parseN和功能中看到我将要描述的内容的预览。parseWhile这些实际上用于通用输入,但我也在此处和此处编写了String类似的示例解析器。
技巧非常简单,您在希望解析器停止的位置插入一个假的输入标记结尾,运行解析器(如果它命中输入标记的假结尾,解析器将会失败),然后删除输入标记的结尾。
显然,这并不像我说的那么容易,但这是一般原则。棘手的部分是:
以使其仍然流动的方式进行。我链接的那个还没有这样做,但是以流方式执行此操作的方法是在上游插入一个管道,该管道计算流过它的字节数,然后在正确的位置插入输入结束标记。
不干扰现有的输入结束标记
这个技巧可以适用于pipes-attoparsec,但我认为最好的解决方案是attoparsec直接包含此功能。但是,如果该解决方案不可用,那么我们可以限制提供给解析器的输入attoparsec。
| 归档时间: |
|
| 查看次数: |
759 次 |
| 最近记录: |