高效解析Java字节流

5

我正在解析一个已知字节数的数据。然而,前两个字节代表一个数字,接下来的一个字节代表一个仅有一个字节的数字,但是可能接下来的四个字节都代表一个很大的数字。除了我目前使用的方法,是否有更好的解析数据的方式?

    switch (i) {
            //Status
            case 2:
                temp[3] = bytes[i];
                break;
            case 3:
                temp[2] = bytes[i];
                ret.put("Status", byteArrayToInt(temp).toString());
                break;
            //Voltage
            case 4:
                temp[3] = bytes[i];
                break;
            case 5:
                temp[2] = bytes[i];
                ret.put("Voltage", byteArrayToInt(temp).toString());
                break;
            //Lowest Device Signal
            case 6:
                temp[3] = bytes[i];
                break;
            case 7:
                temp[2] = bytes[i];
                ret.put("Lowest Device Signal", byteArrayToInt(temp).toString());
                clearBytes(temp);
                break;

我正在遍历字节数组,并有一个开关知道哪些字节应该放在哪个位置,例如我知道第二个和第三个字节应该放在状态码中。因此我将它们合并成一个int。临时字节数组是byte [] temp = new byte [4]。 有更好的方法吗?


你可以用临时编码自行完成,或尝试使用某种通用解析器。如果情况不会比上面更复杂,并且你不会频繁修改它,则上面的临时方法可能比想出更通用的方案更容易。 - Hot Licks
i的值是从byte[]本身读取的吗?您能提供有关数据结构的其他信息吗? - c.s.
创建字符串并将其放入Map中比解析要昂贵得多。可能高达10倍。我会担心避免首先创建对象或设置Map。 - Peter Lawrey
2个回答

11

ByteBuffer 可以处理此事。

byte[] somebytes = { 1, 5, 5, 0, 1, 0, 5 };
ByteBuffer bb = ByteBuffer.wrap(somebytes);
int first = bb.getShort(); //pull off a 16 bit short (1, 5)
int second = bb.get(); //pull off the next byte (5)
int third = bb.getInt(); //pull off the next 32 bit int (0, 1, 0, 5)
System.out.println(first + " " + second + " " + third);

Output
261 5 65541

你还可以使用 get(byte[] dst, int offset, int length) 方法提取任意数量的字节,然后将字节数组转换为所需的数据类型。


+1 我会继续读取,直到你获得了预期的所有字节。 - Peter Lawrey
我现在得到的数值与以前不同,不确定出了什么问题。parsedData.put("Status", status); parsedData.put("Voltage", String.valueOf(buffer.getShort())); parsedData.put("Lowest Device Signal", String.valueOf(buffer.getShort())); parsedData.put("Lowest Device Signal Address", String.valueOf(buffer.get())); - Dylan Holmes
您可能还需要在获取“status”之前调用buffer.getShort()一次来跳过前两个字节。请记住,您的switch case从数组索引2开始,而不是0。 - Syon
聪明的人,那就是问题所在,不过我自己一段时间前就已经解决了。但你能注意到这点真不错。 - Dylan Holmes

5
您可以使用DataInputStream读取多个字节作为int或short。看起来您每次只使用2个字节,因此应该读取short而不是int(在Java中始终为4个字节)。
但在下面的代码示例中,我将使用您的描述“但是,前两个字节表示某个数字,然后下一个字节表示仅为一个字节的数字,但接下来可能的四个字节都表示一个数字”。
DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes));

//the first two bytes represent some number
ret.put("first", Short.toString(in.readShort()));
//next one represents a number that's only one byte
ret.put("second", Byte.toString(in.readByte()));
//next four all represent one number
ret.put("Lowest Device Signal", Integer.toString(in.readInt()));

我正在从BluetoothSocket读取数据,有时候我会读取4个字节,有时候我会读取3个字节,这里只列出了前7种情况。(总共有58种情况) - Dylan Holmes
它仍将以大致相同的方式工作,只需在每个情况下调用适当的readXXX()方法即可。 - dkatzel
@user2635648 扩展DataInputStream并添加readStatus(),readVoltage()方法,最后添加readRecord()或者是你正在读取的整个数据块。 - c.s.
它是如何知道只读取前两个字节的in.readShort()呢? - Dylan Holmes
readShort() 表示将 2 个连续的字节读取为单个值。readInt() 表示将 4 个连续的字节读取为单个值。 - dkatzel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接