在JAVA中编写一个位读取器(32位小端最多到最低位的打包)

时间:2015-04-02 09:53:20

标签: java bit-manipulation endianness

无论我如何折腾并转动字节和比特,我只是无法让这个工作。我理解endianess相当不错,而MSB(最重要的一点)在某种程度上。但我似乎无法将两者放在一起。

目前,我将数据放在普通的JAVA byte []数组中。位流内的符号使用32位little-endian最高至最低有效位打包进行编码。我需要在一个位读取器N位读取它,如:

public int read(int bits, boolean advanceReader)

当然要跟踪

private int byteOffset;
private int bitOffset;

问题是将值变为整数,我只能理解如何正确实现:(

编辑:我尝试使用这个Apache Licensed BitReader类添加了小端(我添加到readInt())支持(原始代码:https://raw.githubusercontent.com/jcodec/jcodec/master/src/main/java/org/jcodec/common/io/BitReader.java):

public class BitReader {

protected int deficit;
protected int curInt;
private ByteBuffer bb;
private int initPos;

public BitReader(ByteBuffer bb) {
    this.bb = bb;
    initPos = bb.position();
    curInt = readInt();
    deficit = 0;
}

public final int readInt() {
    if (bb.remaining() >= 4) {
        deficit -= 32;
        final int b1 = bb.get() & 0xff;
        final int b2 = bb.get() & 0xff;
        final int b3 = bb.get() & 0xff;
        final int b4 = bb.get() & 0xff;
        if (bb.order() == ByteOrder.BIG_ENDIAN) {
            return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
        } else {
            return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
        }
    } else {
        return readIntSafe();
    }
}

public int readNBit(int n) {
    if (n > 32) {
        throw new IllegalArgumentException("Can not read more then 32 bit");
    }

    int nn = n;

    int ret = 0;
    if (n + deficit > 31) {
        ret |= (curInt >>> deficit);
        n -= 32 - deficit;
        ret <<= n;
        deficit = 32;
        curInt = readInt();
    }

    if (n != 0) {
        ret |= curInt >>> (32 - n);
        curInt <<= n;
        deficit += n;
    }

    // for(--nn; nn >=0; nn--)
    // System.out.print((ret >> nn) & 1);
    // System.out.println();

    return ret;
}

public int position() {
    return ((bb.position() - initPos - 4) << 3) + deficit;
}

}

也许我在readNBit中遗漏了一些东西,它仍然像大端一样处理?结果就在附近,但不太正确。

一些数据:

  

阵列:
  [0]字节64
  [1]字节1
  [2]字节-32
  [3]字节1
  [4]字节100
  [5]字节20
  [6]字节30
  [7]字节3
  [8]字节47
  [9]字节-91
  [10]字节52
  [11]字节-12
  [12]字节2
  [13]字节-6
  [14]字节11
  [15]字节-24
  [16]字节41
  [17]字节98
  [18]字节-124
  [19]字节52
  赤字= 21
  位置(字节数组)= 12

读32位让我:-1510145665

显然应该是:-1510145696

byte[] array = {
  64, 1, -32, 1,
  100, 20, 30, 3,
  47, -91, 52, -12,
  2, -6, 11, -24,
  41, 98, -124, 52
};
ByteBuffer bArray = ByteBuffer.wrap(array);
bArray.order(ByteOrder.LITTLE_ENDIAN);
BitReader bitReader = new BitReader(bArray);
bitReader.readNBit(32);
bitReader.readNBit(32);
bitReader.readNBit(21);
int luku = bitReader.readNBit(32);

Luku == -1510145665,我认为应该是-1510145696。

1 个答案:

答案 0 :(得分:0)

47, -91, 52, -12 to little endian int => 11110100001101001010010100101111
2, -6, 11, -24 to little endian int => 11101000000010111111101000000010

消耗21位后,以下32位10100101111111010000000101111111为十六进制0xA5FD017F,十进制为2784821631

System.out.println(0xA5FD017F);
long l = 2784821631L;
System.out.println((int)l);

都会给你-1510145665。所以原始代码是正确的。

当您一次读取少于32位时,将输入字节数组视为小端的更改可能不是您想要的。您可能必须一次读取一个字节而不是使用现有的readInt()