在 Parsec 中流式读取和写入
Streaming reads and writes in Parsec
给定 parsec
库中的 Parser
,什么是流式读取(从输入文件)和写入(将已解析的 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"(从 [=41 读取行=] 并写到 digitsOut.txt)。鉴于 parseFromFile
签名,在我们看到所有输入之前,我们似乎无法延迟流式传输并将输出通过管道传输到输出文件。这是类型签名:
parseFromFile :: Parser a -> String -> IO (Either ParseError a)
所以,为了判断是否有错误,似乎需要整个输入。如果我没记错的话,代码在看到所有输入之前不能写入输出。是否有另一种使用 Parsec
进行流式输入和输出的方法(如果可以避免,则不是 AttoParsec
- 我想访问 Parsec
的错误报告)。我要解析的文件很大 (50+GB)。因此,我需要将解析器代码与流式输入和输出连接起来。如果有好的例子,不胜感激。
更新
刚刚从 AttoParsec
documentation 中发现 Parsec
不能增量消耗输入。因此,无法在 Parsec
中进行流式传输。我现在将解析器重构为 AttoParsec
.
Parsec 通常专注于少量的复杂数据。想想编程和标记语言、困难的二进制格式等。这有两个作用:
- 它针对错误消息(复杂数据需要)优化性能(少量数据不需要)
- 它不进行流式解析(同样,少量数据不需要)
Attoparsec 采用相反的方法:它专注于大量(相对)简单的数据。想想挖掘日志文件、读取测量值、从互联网收集数据流等等。这有两个作用:
- 针对错误消息的性能进行了优化
- 它是围绕流构建的
使用 Attoparsec 进行流式解析的原理是您选择解析某些内容,当它消耗完您的所有输入时,它将 return 一个值,表明它期望收到更多输入。
Note well, however, a huge file might still take up huge amounts of memory even when you use Attoparsec to parse it. This is because Attoparsec (as opposed to Parsec) always backtracks when it fails to match a parser, so it can't throw away "already parsed" data because it might be needed later.
The solution to this is, as hinted in the comments, to write a parser for only a single section of your data (something like a line of a log file) and then run that parser repeatedly from Haskell code for each line of your log file. This allows parsing to take constant space.
Given parseFromFile signature, it doesn't seem we can stream lazily and pipe the output to an output file until we have seen all of the input. [...] So, it seems in order to determine whether there is an error or not, it will need the whole input. If I am not mistaken, the code can't write the output until it has seen all the input.
这对于 Attoparsec 和 Parsec 都是一样的。虽然 Attoparsec 可以让您更好地控制输入的增量消耗,但它仍然无法让解析器表达输出的增量生产。解析器必须 运行 完成才能报告成功,获得解析器输出的唯一方法是通过表示解析器成功这一事实的数据构造函数。
给定 parsec
库中的 Parser
,什么是流式读取(从输入文件)和写入(将已解析的 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"(从 [=41 读取行=] 并写到 digitsOut.txt)。鉴于 parseFromFile
签名,在我们看到所有输入之前,我们似乎无法延迟流式传输并将输出通过管道传输到输出文件。这是类型签名:
parseFromFile :: Parser a -> String -> IO (Either ParseError a)
所以,为了判断是否有错误,似乎需要整个输入。如果我没记错的话,代码在看到所有输入之前不能写入输出。是否有另一种使用 Parsec
进行流式输入和输出的方法(如果可以避免,则不是 AttoParsec
- 我想访问 Parsec
的错误报告)。我要解析的文件很大 (50+GB)。因此,我需要将解析器代码与流式输入和输出连接起来。如果有好的例子,不胜感激。
更新
刚刚从 AttoParsec
documentation 中发现 Parsec
不能增量消耗输入。因此,无法在 Parsec
中进行流式传输。我现在将解析器重构为 AttoParsec
.
Parsec 通常专注于少量的复杂数据。想想编程和标记语言、困难的二进制格式等。这有两个作用:
- 它针对错误消息(复杂数据需要)优化性能(少量数据不需要)
- 它不进行流式解析(同样,少量数据不需要)
Attoparsec 采用相反的方法:它专注于大量(相对)简单的数据。想想挖掘日志文件、读取测量值、从互联网收集数据流等等。这有两个作用:
- 针对错误消息的性能进行了优化
- 它是围绕流构建的
使用 Attoparsec 进行流式解析的原理是您选择解析某些内容,当它消耗完您的所有输入时,它将 return 一个值,表明它期望收到更多输入。
Note well, however, a huge file might still take up huge amounts of memory even when you use Attoparsec to parse it. This is because Attoparsec (as opposed to Parsec) always backtracks when it fails to match a parser, so it can't throw away "already parsed" data because it might be needed later.
The solution to this is, as hinted in the comments, to write a parser for only a single section of your data (something like a line of a log file) and then run that parser repeatedly from Haskell code for each line of your log file. This allows parsing to take constant space.
Given parseFromFile signature, it doesn't seem we can stream lazily and pipe the output to an output file until we have seen all of the input. [...] So, it seems in order to determine whether there is an error or not, it will need the whole input. If I am not mistaken, the code can't write the output until it has seen all the input.
这对于 Attoparsec 和 Parsec 都是一样的。虽然 Attoparsec 可以让您更好地控制输入的增量消耗,但它仍然无法让解析器表达输出的增量生产。解析器必须 运行 完成才能报告成功,获得解析器输出的唯一方法是通过表示解析器成功这一事实的数据构造函数。