在Java中解析二进制数据 - 高并发,单线程

3
我需要在Java中解析(转换和写入)一个大型二进制文件(超过内存大小)。此外,我需要在单线程情况下尽可能高效地执行操作。最后,读取的格式非常结构化,因此最好有某种解析器库(使代码接近复杂规范)。
如果重要,则解析所需的前瞻量应该很小。
我的问题是:
- 对于单线程、高容量应用程序,nio与io有多重要? - 是否有任何好的二进制数据解析器库? - 解析器对流式转换的支持如何(我想能够在解析时将数据流传输到某个输出 - 我不想在编写输出之前构造整个解析树)?
在nio方面,我怀疑nio不会有太大帮助,因为我可能受到磁盘限制(而且由于只有一个线程,所以没有损失,简单阻塞)。此外,我认为基于io的解析器更常见。
4个回答

3
让我试着解释一下Preon是如何解决你提到的所有问题的:
“我需要在Java中解析(转换和写入)一个大型二进制文件(比内存还大)。”
这正是Preon被创建的原因。您希望能够处理整个文件,而不需要将其全部加载到内存中。尽管如此,程序模型会给您一个指向数据结构的指针,该数据结构看起来完全在内存中。但是,Preon会尽可能地懒惰地加载数据。
为了说明这意味着什么,想象一下,在您的数据结构中的某个地方,您有一个使用恒定大小进行二进制表示的事物集合;假设每个元素都将以20个字节编码。然后,Preon首先根本不会在内存中加载该集合,如果您获取超出该集合的数据,它将根本不会触及您的编码表示的那个区域。但是,如果您选择该集合的第300个元素,它将(而不是解码所有元素直到第300个元素),计算该元素的偏移量,并立即跳转到那里。
从外部看,好像你有一个完全填充的列表的引用。从内部来看,只有在你要求它时才会去获取列表的元素。(除非你指示Preon以不同的方式处理)。
“我也需要在单个线程中尽可能高效地完成操作。”我不确定你所说的“高效”是什么意思。它可能是指在内存消耗方面高效,或者在磁盘IO方面高效,或者你可能是指它应该非常快。我认为可以说Preon旨在在易于编程模型、内存使用和其他一些问题之间取得平衡。如果你真的需要以顺序方式遍历所有数据,那么也许有更有效的计算资源方面的方法,但我认为这将以“易于编程”的代价为代价。
最后,被读取的格式非常结构化,因此最好有某种解析器库(这样代码就接近复杂的规范)。
我实现对Java字节码的支持方式是仅仅阅读字节码规范,然后将其中提到的所有结构直接映射到带有注释的Java类中。我认为Preon非常接近你所寻找的内容。
你可能还想查看preon-emitter,因为它允许您生成带注释的十六进制转储(例如在此Java类文件的十六进制转储示例中),这是我在任何其他库中都没有看到的功能。(提示:确保您将鼠标悬停在十六进制数字上。)
对于它生成的文档也是如此。目标一直是确保它创建的文档可以像那样发布到维基百科。虽然它可能还不完美,但我对它目前能够做到的感到满意。(例如:这是Java类文件规范生成的文档示例。)
引用:“解析所需的前瞻量应该很小,如果这很重要。”
好的,很好。实际上,这对Preon来说甚至是至关重要的。Preon不支持前瞻。但它确实支持回溯。(也就是说,有时候编码机制的一部分是由之前读取的数据驱动的。Preon允许您声明指向之前读取数据的依赖关系。)
有没有适用于二进制数据的好的解析器库?
Preon! ;-)
解析器对流转换的支持如何(我想能够在解析过程中将正在解析的数据流传输到某些输出 - 我不想在编写输出之前构建整个解析树)?
正如我上面所概述的,Preon在您开始处理它之前并不会将整个数据结构构建在内存中。所以,在这个意义上,你是好的。然而,Preon中没有任何支持转换作为一等公民的东西,它对编码的支持也是有限的。

在nio方面,我怀疑nio不会有太大的帮助,因为我很可能受到磁盘限制(而且由于是单线程,只需简单地阻塞即可)。此外,我认为基于io的解析器更为常见。

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


3

关于NIO和IO,你是正确的,选择IO应该是正确的选择 - 复杂度较低,面向流等。

对于二进制解析库,请查看Preon


第一眼看,Preon看起来很酷,但可能需要在内存中构建解析对象,而不是再次将数据流出? - andrew cooke
不确定Preon是否支持流式传输 - 但我假设这个非常大的二进制文件不是一个结构?我的意思是它包含多个具有相同结构或甚至不同结构的条目/记录。在这种情况下,您可以逐个读取记录并且丢弃之前读取的条目,对吗?还有 - 由于流式传输是一个要求 - 看看http://topcoder.com/home/blog/component/binary-stream-parser/ 是否更合适。 - kjp
1
Preon不会急切地将所有对象加载到内存中。它只会在需要时才这样做,并且在不需要时不会自己保留它。请参见下面我更详细的解释。 - Wilfred Springer

2
使用内存映射文件,您可以在不担心内存问题的情况下快速读取它。

0

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接