3

我需要在 Java 中解析(并转换和写入)一个大型二进制文件(大于内存)。我还需要在单个线程中尽可能高效地执行此操作。最后,被读取的格式非常结构化,所以最好有某种解析器库(这样代码接近复杂的规范)。

如果重要的话,解析所需的前瞻量应该很小。

所以我的问题是:

  • nio v io 对于单线程、大容量应用程序有多重要?

  • 有没有好的二进制数据解析器库?

  • 解析器对流式转换的支持程度如何(我希望能够在解析期间将正在解析的数据流式传输到某些输出 - 我不想在写出之前在内存中构建整个解析树)?

在 nio 方面,我怀疑 nio 不会有太大帮助,因为我的磁盘可能有限(而且由于它是单线程,因此简单阻塞不会有任何损失)。另外,我怀疑基于 io 的解析器更常见。

4

4 回答 4

3

在 NIO 与 IO 上,你是对的,使用 IO 应该是正确的选择——复杂性更低、面向流等。

对于二进制解析库 - 签出Preon

于 2012-06-27T16:28:38.280 回答
3

让我试着解释一下 Preon 是否以及如何解决您提到的所有问题:

我需要在 Java 中解析(并转换和写入)一个大型二进制文件(大于内存)。

正是创建 Preon 的原因。您希望能够处理整个文件,而不会将其完全加载到内存中。尽管如此,程序模型还是为您提供了一个指向似乎完全在内存中的数据结构的指针。但是,Preon 会尽量延迟加载数据。

为了解释这意味着什么,想象一下在你的数据结构的某个地方,你有一个以二进制表示形式编码的具有恒定大小的东西的集合;假设每个元素都将被编码为 20 个字节。然后 Preon 首先根本不会将该集合加载到内存中,如果您要获取该集合之外的数据,它根本不会触及您编码表示的那个区域。但是,如果您选择该集合的第 300 个元素,它将(而不是解码所有元素直到第 300 个元素),计算该元素的偏移量,并立即跳转到那里。

从外部看,就好像您引用了一个完全填充的列表。从内部,如果您要求,它只会抓取列表中的一个元素。(然后立即忘记它,除非您指示 Preon 以不同的方式做事。)

我还需要在单个线程中尽可能高效地执行此操作。

我不确定你所说的效率是什么意思。它可能意味着在内存消耗方面有效,或者在磁盘 IO 方面有效,或者您的意思是它应该非常快。我认为可以公平地说 Preon 旨在在简单的编程模型、内存使用和许多其他问题之间取得平衡。如果您真的需要以顺序方式遍历所有数据,那么也许有一些方法在计算资源方面更有效,但我认为这将以“易于编程”为代价。

最后,被读取的格式非常结构化,所以最好有某种解析器库(这样代码接近复杂的规范)。

我实现对 Java 字节码的支持的方式是阅读字节码规范,然后将他们在其中提到的所有结构直接映射到带有注释的 Java 类。我认为 Preon 非常接近您正在寻找的东西。

您可能还想查看 preon-emitter,因为它允许您生成数据的带注释的 hexdump(例如在这个 Java 类文件的 hexdump 示例中),这是我在任何其他库中都没有看到的功能. (提示:确保将鼠标悬停在十六进制数字上。)

它生成的文档也是如此。目标一直是确保它创建可以发布到维基百科的文档,就像那样。它可能还不完美,但我对它目前的能力并不不满。(例如:这是为 Java 的类文件规范生成的文档。)

如果重要的话,解析所需的前瞻量应该很小。

嗯,很好。事实上,这对 Preon 来说甚至是至关重要的。Preon 不支持前瞻。它确实支持回顾。(也就是说,有时编码机制的一部分是由之前读取的数据驱动的。Preon 允许您声明指向之前读取的数据的依赖关系。)

有没有好的二进制数据解析器库?

前锋!;-)

解析器对流式转换的支持程度如何(我希望能够在解析期间将正在解析的数据流式传输到某些输出 - 我不想在写出之前在内存中构建整个解析树)?

正如我上面所概述的,Preon 不会您开始处理之前在内存中构建整个数据结构。所以,从这个意义上说,你很好。但是,Preon 中没有任何内容支持作为一等公民的转换,并且它对编码的支持是有限的。

在 nio 方面,我怀疑 nio 不会有太大帮助,因为我的磁盘可能有限(而且由于它是单线程,因此简单阻塞不会有任何损失)。另外,我怀疑基于 io 的解析器更常见。

Preon 使用 NIO,但仅支持内存映射文件。

于 2012-06-29T06:20:26.450 回答
2

使用内存映射文件,您可以阅读它而无需担心您的内存,而且速度很快。

于 2012-06-27T16:30:01.233 回答
0

我认为你是正确的 NIO vs IO 除非你有小端数据,因为 NIO 可以本地读取小端。

我不知道任何快速的二进制解析器,通常你想直接调用 NIO 或 IO。内存映射文件有助于从单个线程写入,因为您不必在写入时刷新它。(但使用起来会比较麻烦)

您可以按照自己的喜好流式传输数据,我不认为有任何问题。

于 2012-06-27T16:31:32.887 回答