有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我需要在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) 个答案

  1. # 1 楼答案

    使用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. # 2 楼答案

    在Java中,需要应用bit arithmetics(AND、OR和NOT运算符)来更改或读取字节内的单个位。算术运算符是&;,|还有~

  3. # 3 楼答案

    我听说了Preon的好消息

  4. # 4 楼答案

    Java中有一个内置的byte类型,您可以使用InputStream#read(byte[])读入byte[]缓冲区,然后使用OutputStream#write(byte[], int, int)写入输出流,所以这没有问题

    关于你的信息——正如你正确指出的,你一次得到的最微小的信息是一个字节,因此你必须先将你的信息格式分解成8位的块:

    假设您的消息位于名为data的字节[]中。我也假设没有什么结尾

    uint32的长度为32位->;这是四个字节。(在Java中解析时要小心,Java整数和long是有符号的,你需要处理它。避免麻烦的一个简单方法是使用long。数据[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 |=128=0x80)

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

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

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

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

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

    构建uint32的过程与第一个示例中的过程相同。等等等等

  5. # 5 楼答案

    为了补充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, ....)