给定Parser
库中的parsec
,有什么好的方法可以进行流式读取(来自输入文件)和写入(将已解析的blob / line附加到输出文件)。以下是Text.Parsec.ByteString
的示例:
main = do{ result <- parseFromFile numbers "digits.txt"
; case result of
Left err -> print err
Right xs -> print (sum xs)
}
以上示例仅从“digits.txt”读取,并且在看到所有输入之前不会有输出。让我们假设我们不是收集所有值并进行上面的缩减(sum
),而是希望以流方式将所有xs
写入“digitsOut.txt”(从digits.txt读取行)并写出digitsOut.txt)。给定parseFromFile
签名,在我们看到所有输入之前,似乎我们不能懒惰地流输出并将输出传递给输出文件。这是类型签名:
parseFromFile :: Parser a -> String -> IO (Either ParseError a)
因此,似乎为了确定是否存在错误,它将需要整个输入。如果我没有弄错,代码在看到所有输入之前都无法写入输出。有没有其他方法可以使用Parsec
(而不是AttoParsec
进行流输入和输出,如果可以避免的话 - 我希望能够访问Parsec
的错误报告。我试图解析的文件很大(50 + GB)。所以,我需要使用流输入和输出来连接解析器代码。如果某个地方有一个很好的例子,那么指针就会受到赞赏。
更新
从AttoParsec
documentation找到,Parsec
无法逐步消耗输入。因此,无法在Parsec
中进行流式传输。我现在将解析器重构为AttoParsec
。
答案 0 :(得分:3)
Parsec通常专注于少量复杂数据。想想编程和标记语言,困难的二进制格式等。这有两个影响:
Attoparsec采用相反的方法:它专注于大量(相对)简单数据。考虑挖掘日志文件,读取测量值,从互联网收集数据流等。这有两个影响:
使用Attoparsec进行流解析的原理是你选择解析一些东西,当它消耗了你所有的输入时,它会返回一个值,表明它期望得到更多的输入。
但是,请注意,即使使用Attoparsec解析它,一个巨大的文件仍可能占用大量内存。这是因为Attoparsec(与Parsec相对)在无法匹配解析器时总是回溯,因此它不能丢弃“已经解析”的数据,因为以后可能需要它。
正如评论中暗示的那样,解决方案是只为数据的一个部分编写解析器(类似于日志文件的一行),然后从Haskell代码中为每一行重复运行该解析器。你的日志文件。这允许解析占用恒定的空间。
答案 1 :(得分:0)
给定parseFromFile签名,看起来我们可以懒得流式传输并将输出传递给输出文件,直到我们看到所有输入。 [...]因此,似乎为了确定是否存在错误,它将需要整个输入。如果我没有弄错的话,代码不能写输出,直到它看到所有的输入。
Attoparsec与Parsec一样都是如此。虽然Attoparsec让您可以更好地控制输入的增量消耗,但仍然无法让解析器表达输出的增量生成。解析器必须在报告成功之前运行完成,获取解析器输出的唯一方法是通过表示解析器成功的数据构造函数。