用Java解析二进制数据 - 高容量,单线程

时间:2012-06-27 16:19:46

标签: java parsing binary

我需要在Java中解析(并转换和写入)一个大的二进制文件(大于内存)。我还需要在单个线程中尽可能高效地完成这项工作。最后,正在读取的格式是非常结构化的,因此最好有一些解析器库(以便代码接近复杂的规范)。

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

所以我的问题是:

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

  • 二进制数据是否有任何好的解析器库?

  • 解析器支持流转换的程度如何(我希望能够在解析过程中将正在解析的数据流式化为某些输出 - 我不希望在写出内容之前在内存中构造整个解析树)?

在nio前面,我怀疑nio不会有太多帮助,因为我可能是磁盘有限(因为它是一个单独的线程,所以简单地阻塞就没有损失)。另外,我怀疑基于io的解析器更常见。

4 个答案:

答案 0 :(得分:3)

在NIO vs IO上,你是对的,使用IO应该是正确的选择 - 更少的复杂性,面向流的等等。

对于二进制解析库 - checkout Preon

答案 1 :(得分:3)

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

  

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

完全为什么要创建Preon。您希望能够处理整个文件而无需将其完全加载到内存中。仍然,程序模型为您提供指向出现的数据结构的指针记忆完全。但是,Preon会尝试尽可能懒散地加载数据。

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

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

  

我还需要在单个线程中尽可能高效地完成这项工作。

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

  

最后,正在阅读的格式非常有条理,所以它会是   很高兴有一些解析器库(以便代码接近   复杂的规范)。

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

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

它生成的文档也是如此。目标始终是确保它创建可以发布到维基百科的文档,就像那样。它可能还不完美,但我并不满足于它目前的能力。 (例如:this is the documentation generated for Java's class file specification。)

  
    

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

  

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

  
    

二进制数据是否有任何好的解析器库?

  

前子! ; - )

  

解析器支持流转换的程度如何(我想成为   能够在解析过程中将正在解析的数据流式传输到某个输出 - I   不希望以前在内存中构造一个完整的解析树   写出来的东西)?

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

  

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:0)

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

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

您可以按照自己喜欢的方式传输数据,我不会预见到任何问题。