【问题标题】:How to parse a very large file in F# using FParsec如何使用 FParsec 在 F# 中解析一个非常大的文件
【发布时间】:2015-07-22 11:50:23
【问题描述】:

我正在尝试使用 FParsec 解析一个非常大的文件。该文件的大小为 61GB,太大而无法保存在 RAM 中,所以如果可能的话,我想生成一个结果序列(即 seq),而不是一个列表。这可以用 FParsec 完成吗? (我想出了一个实际上可以做到这一点的 jerry-rigged 实现,但由于 CharStream.Seek 的 O(n) 性能,它在实践中效果不佳。)

该文件是面向行的(每行一条记录),理论上应该可以一次解析 1000 条记录。 FParsec "Tips and tricks" 部分说:

如果您正在处理大型输入文件或非常慢的解析器,它 可能也值得尝试在单个文件中解析多个部分 并行文件。为了使其有效,必须有一种快速的方法 找到这些部分的起点和终点。例如,如果您 正在解析大型序列化数据结构,格式可能允许 您可以轻松跳过文件中的片段,以便您可以切 将输入分成多个可以解析的独立部分 平行线。另一个例子可能是一种编程语言,其 语法可以轻松跳过完整的类或函数 定义,例如通过找到右大括号或通过解释 缩进。在这种情况下,可能值得不解析 遇到时直接定义,而是跳过 在他们之上,将他们的文本内容推送到队列中,然后进行处理 该队列并行。

这对我来说听起来很完美:我想将每批记录预先解析到一个队列中,然后稍后并行完成它们的解析。但是,我不知道如何使用 FParsec API 完成此操作。如何在不耗尽所有 RAM 的情况下创建这样的队列?

FWIW,如果有人想和我一起尝试,我要解析的文件是 here。 :)

【问题讨论】:

  • 能否举一些记录作为例子
  • 该文件中的每条记录大约有 10K 个字符长,所以我不能在这里粘贴一个,但文件格式规范有一个很好的小例子:samtools.github.io/hts-specs/VCFv4.2.pdf。我正在解析的记录是该示例中的最后 5 条记录 - 以“20”开头的记录。
  • 澄清一下:我知道如何解析记录,而且我的解析器适用于大多数文件。我只是无法将其扩展到巨大的输入。
  • 听起来每条记录都是独立的,对吗?也就是说,您不需要来自过去(或将来)记录的信息来完全解析单个记录。如果是这样,为什么不将行读取为 seq{} 和 Seq.iter parseRecord?让 F#/CLR 担心批处理/缓冲,只关注面向行的记录解析。完成记录后,垃圾收集器应该处理它。我的感觉是,您应该能够以最小的内存占用以这种方式处理超大文件。
  • 我喜欢这个主意。会试一试的。

标签: parsing f# bigdata large-files fparsec


【解决方案1】:

想到的“显而易见”的事情是使用 File.ReadLines 之类的东西预处理文件,然后一次解析一行。

如果这不起作用(您的 PDF 看起来,就像一条记录有几行长),那么您可以使用普通的 FileStream 读取来制作一系列记录或 1000 条记录或类似的东西。这不需要知道记录的详细信息,但如果您至少可以对记录进行分隔,那将很方便。

无论哪种方式,最终都会得到解析器可以读取的惰性序列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-01
    • 1970-01-01
    • 2013-02-28
    • 2015-01-07
    • 2013-03-24
    相关资源
    最近更新 更多