2010-04-19 309 views
13

我需要解析一些简单的二进制文件。 (这些文件包含n个条目,其中包含几个不同大小的有符号/无符号整数等。)用scala解析二进制数据

在我手动解析的时刻。有人知道一个库可以帮助做这种类型的解析吗?

编辑:“手工”意味着我得到数据字节按字节排序到正确的顺序,并将其转换为一个Int/Byte等。另外一些数据是无符号的。

回答

7

我以前用过sbinary这个库,它非常好。文件有点稀疏,但我建议先看看旧的wiki page,因为这给你一个起点。然后检查测试规格,因为这给你一些非常好的例子。

sbinary的主要好处是它为您提供了一种将每个对象的线格式描述为Format对象的方法。然后,您可以将这些格式化的类型封装在更高级别的Format对象中,只要您将它作为隐式对象包含在当前范围中,Scala就会完成查找该类型的所有繁重工作。

正如我下面所说,我现在建议人们使用scodec而不是sbinary。作为如何使用scodec一个例子,我将实现如何读取在下面的C结构的存储器的二进制表示:

struct ST 
{ 
    long long ll; // @ 0 
    int i;  // @ 8 
    short s;  // @ 12 
    char ch1;  // @ 14 
    char ch2;  // @ 15 
} ST; 

甲匹配的Scala情况下类将是:

case class ST(ll: Long, i: Int, s: Short, ch1: String, ch2: String) 

我只是说我们存储的是字符串而不是字符,并且我会说它们是结构体中的UTF-8字符,这让我对自己变得更容易一些。我也没有在这个架构上处理endian细节或者long和int类型的实际大小,而只是假设它们分别是64和32。

Scodec解析器通常使用组合器从较低级别构建更高级别的解析器。因此,下面我们将定义一个解析器,它包含一个8字节的值,一个4字节的值,一个2字节的值,一个1字节的值和一个1字节的值。这个组合的回报率是一个元组编解码器:

val myCodec: Codec[Long ~ Int ~ Short ~ String ~ String] = 
    int64 ~ int32 ~ short16 ~ fixedSizeBits(8L, utf8) ~ fixedSizeBits(8L, utf8) 

然后我们就可以通过调用它的xmap功能它有两个功能,一是把元组编解码器到目的地类型将其转化成的ST案例类和另一个函数把目标类型,使之成为元组形式:

val stCodec: Codec[ST] = myCodec.xmap[ST]({case ll ~ i ~ s ~ ch1 ~ ch2 => ST(ll, i, s, ch1, ch2)}, st => st.ll ~ st.i ~ st.s ~ st.ch1 ~ st.ch2) 

现在,你可以使用的编解码器,像这样:

stCodec.encode(ST(1L, 2, 3.shortValue, "H", "I")) 
res0: scodec.Attempt[scodec.bits.BitVector] = Successful(BitVector(128 bits, 0x00000000000000010000000200034849)) 

res0.flatMap(stCodec.decode) 
=> res1: scodec.Attempt[scodec.DecodeResult[ST]] = Successful(DecodeResult(ST(1,2,3,H,I),BitVector(empty))) 

我鼓励你看看Scaladocs,而不是在指南中,因为Scaladocs中有更多的细节。该指南是非常基础的一个良好开端,但它并没有深入到组成部分,但Scaladocs覆盖非常好。

+0

如果有人看到这个,我现在推荐http://scodec.org。与sbinary相同的好处,但更好的支持和更多的功能。 – Aaron 2015-10-05 14:05:17

+0

我仍然不知道如何使用'sbinary'或'scodec'库来解析其他地方定义的二进制协议,即使在通过各自的文档之后,它们也是一系列C结构。你能详细说明一下,最好是用代码片段吗? – nodakai 2015-12-18 20:56:21

+0

我刚刚为C风格的结构添加了scodec解析的示例。希望有所帮助! – Aaron 2015-12-23 02:34:51

4

我不知道你的意思是用“手”,但使用简单DataInputStream(apidoc here)是相当简洁明了什么:

val dis = new DataInputStream(yourSource) 

dis.readFloat() 
dis.readDouble() 
dis.readInt() 
// and so on 

从另一个SO question摘自:http://preon.sourceforge.net/,它应该是一个框架来做二进制编码/解码..看看它是否有你需要的功能

+1

恩情怎么样?那么pascal字符串(字节大小,然后字符串ascii)呢?我相信他正在寻找类似python的'struct.pack','struct.unpack'。 – 2010-04-19 14:14:25

+0

没有人愿意为像[BMP文件](https://en.wikipedia.org/wiki/BMP_file_format#DIB_header_.28bitmap_information_header.29)这样的真实世界的例子这样做,尤其是当所有数据成员都没有使用时。 – nodakai 2015-12-18 20:59:04

4

斯卡拉本身没有二进制数据输入库,但java.nio包做了不错的工作。它没有明确处理未签名的数据 - Java也没有,所以你需要弄清楚你想如何管理它 - 但它确实有方便的“get”方法来考虑字节顺序。

1

如果您正在寻找一个基于Java的解决方案,那么我会无耻地插入Preon。您只需在内存中注释Java数据结构,并向Preon请求Codec,即可完成。

+0

https://github.com/preon/preon 我可以在'.scala'文件中使用它,还是需要一个'.java'文件? – nodakai 2015-12-18 21:01:40

1

Byteme是一个解析器组合库,用于执行二进制。你可以尝试用它来完成你的任务。