我需要解析并编码为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
答案 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;,|和〜