Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何解析/编码二进制消息格式?_Java_Binaryfiles - Fatal编程技术网

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

Java 如何解析/编码二进制消息格式?,java,binaryfiles,Java,Binaryfiles,我需要在Java中解析和编码为传统的二进制消息格式。我开始使用DataOutputStream来读/写原语类型,但我遇到的问题是消息格式不能很好地与字节偏移对齐,并且包含位标志 例如,我必须处理这样的消息: +----------+---+---+----------+---------+--------------+ +uint32 +b +b + uint32 +4bit enum+32 byte string+ +----------+---+---+----------+-

我需要在Java中解析和编码为传统的二进制消息格式。我开始使用DataOutputStream来读/写原语类型,但我遇到的问题是消息格式不能很好地与字节偏移对齐,并且包含位标志

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

+----------+---+---+----------+---------+--------------+
+uint32    +b   +b + uint32   +4bit enum+32 byte string+
+----------+---+---+----------+---------+--------------+
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
}
Codec<DataStructure> codec = Codecs.create(DataStructure.class)
DataStructure data = Codecs.decode(codec, ....)
其中(b)是一位标志。问题在于java原语类型不与字节边界对齐,因此我无法使用DataOutputStream对其进行编码,因为我可以写入的最低级别类型是字节

是否有任何标准或第三方库用于处理任意位级别的消息格式

编辑: 感谢@Software Monkey迫使我更仔细地查看我的规范。我使用的规范实际上在字节边界上对齐,因此DataOutputStream是合适的。考虑到我最初的问题,我会同意@emboss提出的解决方案

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

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

我听说过一些好东西。

Java中有一个内置的
字节类型,您可以使用它读入
字节[]
缓冲区,然后使用它写入输出流,所以这没有问题

关于您的消息-正如您正确指出的,您每次获得的最小信息位是一个字节,因此您必须首先将消息格式分解为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);
第二位:(二进制掩码| 0 1 0 0 0 |=64=0x40)

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

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

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

为了补充pholser的答案,我认为Preon版本应该是这样的:

+----------+---+---+----------+---------+--------------+
+uint32    +b   +b + uint32   +4bit enum+32 byte string+
+----------+---+---+----------+---------+--------------+
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
}
Codec<DataStructure> codec = Codecs.create(DataStructure.class)
DataStructure data = Codecs.decode(codec, ....)
…但事实上,你可以通过使用Preon的支持让你的生活更加轻松

为它创建一个
编解码器
,并使用它对一些数据进行解码如下:

+----------+---+---+----------+---------+--------------+
+uint32    +b   +b + uint32   +4bit enum+32 byte string+
+----------+---+---+----------+---------+--------------+
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
}
Codec<DataStructure> codec = Codecs.create(DataStructure.class)
DataStructure data = Codecs.decode(codec, ....)
Codec Codec=Codecs.create(DataStructure.class)
数据结构数据=编解码器。解码(编解码器…)
将使用脚本解析消息

  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);

这是一个非常不寻常的规范;根据我多年的有线通信经验,我会质疑您的布局是否正确。特别是,您的第二个uint32和32字节字符串从一个字节的中间开始-当然不是!对于有线协议,将标志打包为字节、字或dword中的位并不罕见,但会有未对齐的完整字节,单词和dwords显然是愚蠢的。确认,你是对的。我对消息格式的印象很差,现在我看到“备用位”在位标志和下一个在字节边界上对齐的原语之间啮合。谢谢!你应该用真正的规范更新你的问题(还有一个注释,这样浮雕的答案就不会显得不合适)。如果你想尝试第三部分c库,我不确定。它可以在一个字节的开头抓取第一个位标志。这仍然会留下一些奇怪的逻辑来处理,比如说一个uint32,它从4位开始变成一个字节,然后再继续3.5个字节。该网站似乎相当混乱;无法找到任何示例-只是显示这是一个导语和一个空白的内容体。不确定你检查了哪个网站,但对我来说,似乎没有被破坏。你可能想检查Preon上的幻灯片,以及相关的更多文档。(我同意示例代码实际上应该包含在Maven生成的文档中。)上面的uint32提取代码因字节值<0而被破坏;您需要使用
((data[x]&0xFF)@Software Monkey:您说得对,谢谢!我会更新帖子的。