从字节数组中获取位值

时间:2017-09-11 19:29:01

标签: java bit-manipulation bit

我有一个字节数组,我需要从中读取特定的位并转换为int(参见下面的字节数组结构)。即使我想要读取的位信息是3个字节,我尝试读取4个字节(6-9)作为整数,然后使用bits或bitsValue方法从该整数值读取位,但不知怎的,我无法看到正确的位操作的值。凭借我的专业知识,我很确定我做错了什么。 有人可以建议我正确地做,以及为什么它不起作用。在此先感谢!!

Byte数组采用Little Endian格式。

第0个字节 - 一些值 第一字节 - 一些价值 第2 - 第5字节 - 一些价值 第6到第9个字节 - 前18位表示某个值                 - 接下来的5位代表一些值                 - 下一位代表一些值                 - 最后8位表示某个值

 public class Test {

  public static void main(String... dataProvider) {
    String s = "46 00 ef 30 e9 08 cc a5 03 43";
    byte[] bytes = new byte[s.length()];
    bytes = hexStringToByteArray(s);

    int bytePointer = 0;
    int msgType = getIntFromSingleByte(bytes[bytePointer]); // 0th byte
    int version = getIntFromSingleByte(bytes[++bytePointer]); // 1st byte
    int tickInMS = getIntValue(bytes, ++bytePointer); // 2nd-5th bytes

    bytePointer = bytePointer + 4;
    int headr = getIntValue(bytes, bytePointer); // 6th-9th bytes
    int utcTime = bits(headr, 0, 18); // 6th-9th bytes - 18 bits
    int reserved = bits(headr, 18, 5); // 6th-9th bytes- 5 bits
    int reportOrEvent = bits(headr, 23, 1); // 6th-9th bytes - 1 bits
    int reportId = bitsValue(headr, 24, 32); // 6th-9th- 8 bits

  }



    public static int getIntFromSingleByte(byte data) {
        return (data & 0xFF);
      }

      public static int getIntValue(byte[] bytes, int startPosition) {
        byte[] dest = new byte[4];
        System.arraycopy(bytes, startPosition, dest, 0, dest.length);
        return toInt(dest);
      }

      // took from Stack overflow
      static int bits(int n, int offset, int length) {
        // shift the bits rightward, so that the desired chunk is at the right end
        n = n >> (31 - offset - length);
        // prepare a mask where only the rightmost `length` bits are 1's
        int mask = ~(-1 << length);
        // zero out all bits but the right chunk
        return n & mask;
      }

      public static int bitsValue(int intNum, int startBitPos, int endBitPos) {
        // parameters checking ignored for now
        int tempValue = intNum << endBitPos;
        return tempValue >> (startBitPos + endBitPos);
      }

      public static byte[] hexStringToByteArray(final String s) {
        String[] splits = s.split(" ");
        final byte[] data = new byte[splits.length];
        for (int i = 0; i < splits.length; i++) {
          char first = splits[i].length() < 2 ? '0' : splits[i].charAt(0);
          char second = splits[i].length() < 2 ? splits[i].charAt(0) : splits[i].charAt(1);
          data[i] = (byte) ((Character.digit(first, 16) << 4) + Character.digit(second, 16));
        }
    return data;
  }

  public static int toInt(byte[] data) {
    if (data == null || data.length != 4)
      return 0x0;

    return (int) ((0xff & data[0]) << 24 | (0xff & data[1]) << 16 | (0xff & data[2]) << 8
        | (0xff & data[3]) << 0);
  }
}

2 个答案:

答案 0 :(得分:2)

将输入数据包装在ByteBuffer中将简化解析并允许您根据需要调整字节顺序。

您的bits方法错误。常数31应为32.此外,该方法使用MSB 0位编号,这对于小端数据是奇数。您应该确认您的输入是使用此位编号方案记录的。

您的bitsValue方法也是错误的。也可以在修复后使用bits

此代码更简单,并正确提取位字段:

public static void main(String... args) {
    String s = "46 0 79 37 a8 3 9f 37 1 43 eb 7a f 3 3 fe c4 1 c5 4 c5 5e";
    byte[] input = hexStringToByteArray(s);

    // Wrap the input in a ByteBuffer for parsing. Adjust endianness if necessary.
    ByteBuffer buffer = ByteBuffer.wrap(input).order(ByteOrder.BIG_ENDIAN);

    int msgType = buffer.get() & 0xff;
    int version = buffer.get() & 0xff;
    int tickInMS = buffer.getInt();
    int header = buffer.getInt();

    int utcTime = bits(header, 0, 18);          // 6th-9th bytes - 18 bits
    int reserved = bits(header, 18, 5);         // 6th-9th bytes - 5 bits
    int reportOrEvent = bits(header, 23, 1);    // 6th-9th bytes - 1 bit
    int reportId = bits(header, 24, 8);         // 6th-9th bytes - 8 bits

    System.out.printf("utc: %d, report? %d, id: %d\n", utcTime, reportOrEvent, reportId);
}

/**
 * Extract a bit field from an int. Bit numbering is MSB 0.
 */
public static int bits(int n, int offset, int length) {
    return n >> (32 - offset - length) & ~(-1 << length);
}

答案 1 :(得分:0)

您应该使用数字的二进制字符串表示,而不是容易出错的位编码,并执行您的选择&#34;作为字符串操作:

String s = "46 00 ef 30 e9 08 cc a5 03 43";
String[] hexNumbers = s.split(" ");
for(String hexNumber : hexNumbers) {
  String binaryNumber = String.format("%8s", new BigInteger(hexNumber,16).toString(2)).replace(' ','0');
  System.out.print(String.format("value in hex : %s, value in binary: %s",  hexNumber,binaryNumber));
}