Java中字节数组的位移操作

时间:2015-03-11 21:32:09

标签: java bit-manipulation

如何将字节数组n位置向右移位?例如,将一个16字节数组右移29个位置?我在某个地方看过它可以用很长的时间来完成吗?会使用这样的长期工作:

长k1 =从0到7的字节数组

长k2 = 8到15之间的字节数组

然后使用Long.rotateRight(长x,旋转次数)右旋这两个long。如何将两个long连接回一个字节数组?

4 个答案:

答案 0 :(得分:9)

我相信你可以使用java.math.BigInteger来做到这一点,它支持任意大数字的移位。这具有简单的优点,但缺点是不填充到原始字节数组大小,即输入可能是16字节但输出可能只有10等,需要额外的逻辑。

BigInteger方法

byte [] array = new byte[]{0x7F,0x11,0x22,0x33,0x44,0x55,0x66,0x77};

// create from array
BigInteger bigInt = new BigInteger(array);

// shift
BigInteger shiftInt = bigInt.shiftRight(4);

// back to array
byte [] shifted = shiftInt.toByteArray();

// print it as hex
for (byte b : shifted) {
    System.out.print(String.format("%x", b));
}

输出

7f1122334455667   <== shifted 4 to the right. Looks OK

长时间操纵

我不知道你为什么要这样做,因为这会让生活变得更加困难,你必须在K1等左侧出现的位置留空。你会更好使用移位IMO如下所述。我使用了20的移位可被4整除,因此更容易看到半字节在输出中移动。

1)使用ByteBuffer从16字节数组

形成两个长整数
byte[] array = { 0x00, 0x00, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77 };
ByteBuffer buffer = ByteBuffer.wrap(array);
long k1 = buffer.getLong();
long k2 = buffer.getLong();

2)将每个长n位向右移位

int n = 20;

long k1Shift = k1 >> n;
long k2Shift = k2 >> n;

System.out.println(String.format("%016x => %016x", k1, k1Shift));
System.out.println(String.format("%016x => %016x", k2, k2Shift));

0000111122223333 => 0000000001111222
4444555566667777 => 0000044445555666

确定k1中“被推离边缘”的位

long k1CarryBits = (k1 << (64 - n));
System.out.println(String.format("%016x => %016x", k1, k1CarryBits));

0000111122223333 => 2333300000000000

将K1进位加入右侧的K2

long k2WithCarray = k2Shift | k1CarryBits;
System.out.println(String.format("%016x => %016x", k2Shift, k2WithCarray));

0000044445555666 => 2333344445555666

将两个long写回ByteBuffer并提取为字节数组

buffer.position(0);
buffer.putLong(k1Shift);
buffer.putLong(k2WithCarray);
for (byte each : buffer.array()) {
    System.out.print(Long.toHexString(each));
}

000011112222333344445555666

答案 1 :(得分:2)

这是我想出的将字节数组移位一些任意数量的位:

/**
 * Shifts input byte array len bits left.This method will alter the input byte array.
 */
public static byte[] shiftLeft(byte[] data, int len) {
    int word_size = (len / 8) + 1;
    int shift = len % 8;
    byte carry_mask = (byte) ((1 << shift) - 1);
    int offset = word_size - 1;
    for (int i = 0; i < data.length; i++) {
        int src_index = i+offset;
        if (src_index >= data.length) {
            data[i] = 0;
        } else {
            byte src = data[src_index];
            byte dst = (byte) (src << shift);
            if (src_index+1 < data.length) {
                dst |= data[src_index+1] >>> (8-shift) & carry_mask;
            }
            data[i] = dst;
        }
    }
    return data;
}

答案 2 :(得分:1)

1。手动实施

这里是左右移位实现而不使用BigInteger(即没有创建输入数组的副本)和无符号右移(BigInteger当然只支持算术移位)

左移&lt;&lt;

/**
 * Light shift of whole byte array by shiftBitCount bits. 
 * This method will alter the input byte array.
 */
static byte[] shiftLeft(byte[] byteArray, int shiftBitCount) {
    final int shiftMod = shiftBitCount % 8;
    final byte carryMask = (byte) ((1 << shiftMod) - 1);
    final int offsetBytes = (shiftBitCount / 8);

    int sourceIndex;
    for (int i = 0; i < byteArray.length; i++) {
        sourceIndex = i + offsetBytes;
        if (sourceIndex >= byteArray.length) {
            byteArray[i] = 0;
        } else {
            byte src = byteArray[sourceIndex];
            byte dst = (byte) (src << shiftMod);
            if (sourceIndex + 1 < byteArray.length) {
                dst |= byteArray[sourceIndex + 1] >>> (8 - shiftMod) & carryMask;
            }
            byteArray[i] = dst;
        }
    }
    return byteArray;
}

无符号右移&gt;&gt;&gt;

/**
 * Unsigned/logical right shift of whole byte array by shiftBitCount bits. 
 * This method will alter the input byte array.
 */
static byte[] shiftRight(byte[] byteArray, int shiftBitCount) {
    final int shiftMod = shiftBitCount % 8;
    final byte carryMask = (byte) (0xFF << (8 - shiftMod));
    final int offsetBytes = (shiftBitCount / 8);

    int sourceIndex;
    for (int i = byteArray.length - 1; i >= 0; i--) {
        sourceIndex = i - offsetBytes;
        if (sourceIndex < 0) {
            byteArray[i] = 0;
        } else {
            byte src = byteArray[sourceIndex];
            byte dst = (byte) ((0xff & src) >>> shiftMod);
            if (sourceIndex - 1 >= 0) {
                dst |= byteArray[sourceIndex - 1] << (8 - shiftMod) & carryMask;
            }
            byteArray[i] = dst;
        }
    }
    return byteArray;
}

class用于此Project

2。使用BigInteger

请注意BigInteger在内部将字节数组转换为int []数组,因此这可能不是最优化的解决方案:

算术左移&lt;&lt;:

byte[] result = new BigInteger(byteArray).shiftLeft(3).toByteArray();

算术右移&gt;&gt;:

byte[] result = new BigInteger(byteArray).shiftRight(2).toByteArray();

3。外部图书馆

使用Bytes java library *:

添加到pom.xml:

<dependency>
    <groupId>at.favre.lib</groupId>
    <artifactId>bytes</artifactId>
    <version>{latest-version}</version>
</dependency>

代码示例:

Bytes b = Bytes.wrap(someByteArray);
b.leftShift(3);
b.rightShift(3);
byte[] result = b.array();

*完全免责声明:我是开发人员。

答案 3 :(得分:0)

这是一篇旧帖子,但我想更新亚当的答案。 长解决方案可以进行一些调整。

要旋转,请使用&gt;&gt;&gt;而不是&gt;&gt;,因为&gt;&gt;将填充有效位,改变原始值。

第二,printbyte函数在打印时似乎错过00。 请改用它。

private String getHexString(byte[] b) {
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < b.length; i++)
        result.append(Integer.toString((b[i] & 0xff) + 0x100, 16)
                .substring(1));
    return result.toString();
}