如何解析/编码二进制消息格式?

时间:2011-07-28 17:08:02

标签: java binaryfiles

我需要解析并编码为Java中的旧二进制消息格式。我首先使用DataOutputStream来读取/写入基本类型,但我遇到的问题是消息格式与字节偏移不完全对齐并包含位标志。

例如,我必须处理这样的消息:

+----------+---+---+----------+---------+--------------+
+uint32    +b   +b + uint32   +4bit enum+32 byte string+
+----------+---+---+----------+---------+--------------+

其中(b)是一位标志。问题是java原语类型没有与字节边界对齐,所以我无法使用DataOutputStream对此进行编码,因为我可以写的最低级别类型是一个字节。

是否有任何库,标准或第三方处理任意位级消息格式?

编辑: 感谢@Software Monkey强迫我更仔细地查看我的规范。我正在使用的规范确实在字节边界上对齐,因此DataOutputStream是合适的。鉴于我原来的问题,虽然我会选择@emboss提出的解决方案。

编辑: 虽然发现此问题的消息格式是在字节边界上,但我遇到了另一种适用于原始问题的消息格式。这种格式定义了一个6位字符映射,其中每个字符实际上只占用6位,而不是整个字节,因此字符串不在字节边界上对齐。我发现了几个解决这个问题的二进制输出流。像这样:http://introcs.cs.princeton.edu/java/stdlib/BinaryOut.java.html

5 个答案:

答案 0 :(得分:5)

Java中有一个内置byte类型,您可以使用InputStream#read(byte[])读取byte[]缓冲区并使用OutputStream#write(byte[], int, int)写入OutputStream,所以没有那个问题。

关于你的消息 - 正如你所说的那样,你一次获得的最微小的信息是一个字节,所以你必须先将你的消息格式分解为8位块:

假设您的消息位于byte []命名数据中。我也假设小端。

uint32是32位长 - >这是四个字节。 (在Java中解析这个问题时要小心,Java整数和长整数都已签名,你需要处理它。一个避免麻烦的简单方法就是需要很长时间。数据[0]填充位31 - 24,数据[1] 23 - 16,数据[2]位15 - 8和数据[3]位7到0.所以你需要将它们适当地移到左边并用逻辑OR将它们粘合在一起:

long uint32 = ((data[0]&0xFF) << 24) | 
              ((data[1]&0xFF) << 16) | 
              ((data[2]&0xFF) << 8)  | 
               (data[3]&0xFF);

接下来,有两个单位。我想你必须检查它们是“开”(1)还是“关”(0)。为此,您使用位掩码并将您的字节与逻辑AND进行比较。

第一位:(二进制掩码| 1 0 0 0 0 0 0 0 | = 128 = 0x80)

if ( (data[4] & 0x80 ) == 0x80 ) // on

第二位:(二进制掩码| 0 1 0 0 0 0 0 0 | = 64 = 0x40)

if ( (data[4] & 0x40 ) == 0x40 ) // on

要编写下一个uint32,您必须在基础数据的字节边界上组合字节。例如。对于第一个字节,取剩下的6位数据[4],将它们向左移动两个(它们将是uint32的第8位到第2位),并通过移位“添加”第一个(最高的)两个数据[5]它们向右6位(它们将占用uint32的剩余1和0槽)。 “添加”意味着逻辑OR'ing:

byte uint32Byte1 = (byte)( (data[4]&0xFF) << 2 | (data[5]&&0xFF) >> 6);

构建uint32的过程与第一个示例中的过程相同。依此类推。

答案 1 :(得分:5)

使用Java Binary Block Parser解析消息的脚本将是

  class Parsed {
    @Bin int field1;
    @Bin (type = BinType.BIT) boolean field2;
    @Bin(type = BinType.BIT) boolean field3;
    @Bin int field4;
    @Bin(type = BinType.BIT) int enums;
    @Bin(type = BinType.UBYTE_ARRAY) String str;
  }

  Parsed parsed = JBBPParser.prepare("int field1; bit field2; bit field3; int field4; bit:4 enums; ubyte [32] str;").parse(STREAM).mapTo(Parsed.class);

答案 2 :(得分:4)

我听说过有关Preon的好消息。

答案 3 :(得分:4)

只是添加到pholser的答案,我认为Preon版本会是这样的:

class DataStructure {
  @BoundNumber(size="32")  long       first; // uint32
  @Bound                   boolean    second; // boolean
  @Bound                   boolean    third; // boolean
  @BoundNumber(size="32")  long       fourth; // uint32
  @BoundNumber(size="4")   int        fifth; // enum
  @BoundString(size="32")  String     sixth; // string
}

...但实际上,使用Preon的支持for dealing with enumerations directly可以让您的生活更轻松。

为它创建Codec并使用它来解码某些数据将是这样的:

Codec<DataStructure> codec = Codecs.create(DataStructure.class)
DataStructure data = Codecs.decode(codec, ....)

答案 4 :(得分:2)

您需要应用bit arithmetics(AND,OR和AND运算符)来更改或读取Java中一个字节内的单个位。算术运算符是&amp;,|和〜