parsing - "Sub-parsers"在管道-attoparsec

标签 parsing haskell attoparsec haskell-pipes

我正在尝试使用 Haskell 中的 Pipes-attoparsec 来解析二进制数据。涉及管道(代理)的原因是使读取与解析交错,以避免大文件占用大量内存。许多二进制格式基于 block (或 block ),它们的大小通常由文件中的字段描述。我不确定这样一个 block 的解析器叫什么,但这就是我在标题中所说的“子解析器”的意思。我遇到的问题是以简洁的方式实现它们,而不需要占用大量内存。我提出了两种替代方案,但都在某些方面失败了。

替代方案 1 是将 block 读入单独的字节串并为其启动单独的解析器。虽然简洁,但大的 block 会导致高内存使用。

替代方案 2 是在同一上下文中保持解析并跟踪消耗的字节数。这种跟踪很容易出错,并且似乎会感染组成最终 block 解析器的所有解析器。对于格式错误的输入文件,在比较跟踪的大小之前,它还可能通过比大小字段指示的进一步解析来浪费时间。

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

最佳答案

我喜欢称其为“固定输入”解析器。

我可以告诉你pipes-parse将如何做到这一点。您可以预览我将在 parseN 中的 pipes-parse 中描述的内容。和 parseWhile图书馆的职能。这些实际上用于通用输入,但我也编写了类似的示例,例如 String 解析器 herehere .

技巧非常简单,您在希望解析器停止的位置插入一个假的输入标记结尾,运行解析器(如果它命中输入标记的假结尾,解析器将会失败),然后删除输入标记的结尾.

显然,这并不像我说的那么容易,但这是一般原则。棘手的部分是:

  • 以使其仍然有效的方式进行操作。我链接的那个还没有这样做,但是以流方式执行此操作的方法是在上游插入一个管道,该管道对流过它的字节进行计数,然后在正确的位置插入输入结束标记。

  • 不干扰现有的输入结束标记

这个技巧可以适用于pipes-attoparsec,但我认为最好的解决方案是attoparsec直接包含此功能。但是,如果该解决方案不可用,那么我们可以限制提供给 attoparsec 解析器的输入。

关于parsing - "Sub-parsers"在管道-attoparsec,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15439438/

相关文章:

parsing - 计算文件中的行数 - Scala

Haskell 分析 - clock_gettime

haskell - 如何编写 attoparsec 的 takeWhile1 的更通用(但高效)版本?

haskell - 使用 Attoparsec 时输入不完整的问题

perl - 使用 Perl 修改 PHP 文件(可能使用 HTML::TreeBuilder)

java - 如何将具有不同字段计数的分隔文本行解析为对象,同时允许扩展?

haskell - 如何判断一个 monad 是否可交换?

parsing - 使用 attoparsec 对解析后的数据进行操作

c - 从 Gem 覆盖 Ruby 的基本 C 代码

haskell - 您可以对自定义数据类型使用特殊语法,例如在列表中吗?