java Java中字节数组的位移操作

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28997781/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 14:31:32  来源:igfitidea点击:

Bit shift operations on a byte array in Java

javabit-manipulation

提问by k.ken

How do I shift a byte array n positions to the right? For instance shifting a 16 byte array right 29 positions? I read somewhere it can be done using a long? Would using a long work like this:

如何将字节数组向右移动 n 个位置?例如,将 16 字节数组右移 29 个位置?我在某处读到它可以使用 long 来完成吗?会使用这样的长期工作:

Long k1 = byte array from 0 to 7

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

Long k2 = byte array from 8 to 15

Long k2 = 从 8 到 15 的字节数组

Then right rotating these two longs using Long.rotateRight(Long x, number of rotations).How would the two longs be joined back into a byte array?

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

回答by Adam

I believe you can do this using java.math.BigInteger which supports shifts on arbitrarily large numbers. This has advantage of simplicity, but disadvantage of not padding into original byte array size, i.e. input could be 16 bytes but output might only be 10 etc, requiring additional logic.

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

BigInteger approach

大整数方法

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));
}

Output

输出

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

Long manipulation

长时间操纵

I don't know why you'd want to do this as rotateRight() as this makes life more difficult, you have to blank at the bits that appear at the left hand side in K1 etc. You'd be better with using shift IMO as describe below. I've used a shift of 20 as divisible by 4 so easier to see the nibbles move in the output.

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

1) Use ByteBuffer to form two longs from 16 byte array

1) 使用 ByteBuffer 从 16 字节数组形成两个 long

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) Shift each long n bits to the right

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

Determine bits from k1 that "got pushed off the edge"

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

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

0000111122223333 => 2333300000000000

Join the K1 carry bits onto K2 on right hand side

将 K1 进位位连接到右侧的 K2 上

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

0000044445555666 => 2333344445555666

Write the two longs back into a ByteBuffer and extract as a byte array

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

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

000011112222333344445555666

回答by Matt Wolfe

Here is what I came up with to shift a byte array by some arbitrary number of bits left:

这是我想出的将字节数组左移任意数量的位的方法:

/**
 * 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;
}

回答by Patrick Favre

1. Manually implemented

1.手动实现

Here are left and right shift implementation without using BigInteger(ie. without creating a copy of the input array) and with unsigned right shift (BigIntegeronly supports arithmetic shifts of course)

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

Left Shift <<

左移<<

/**
 * 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;
}

Unsigned Right Shift >>>

无符号右移 >>>

/**
 * 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;
}

Used in this classby this Project.

使用这个的这个项目

2. Using BigInteger

2. 使用 BigInteger

Be aware that BigIntegerinternally converts the byte array into an int[] array so this may not be the most optimized solution:

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

Arithmetic Left Shift <<:

算术左移<<:

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

Arithmetic Right Shift >>:

算术右移>>:

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

3. External Library

3. 外部图书馆

Using the Bytes java library*:

使用字节 java 库*:

Add to pom.xml:

添加到 pom.xml:

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

Code example:

代码示例:

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

*Full Disclaimer: I am the developer.

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

回答by Maxi Wu

The is an old post, but I want to update Adam's answer. The long solution works with a few tweak.

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

In order to rotate, use >>> instead of >>, because >> will pad with significant bit, changing the original value.

为了旋转,使用 >>> 而不是 >>,因为 >> 将填充有效位,改变原始值。

second, the printbyte function seems to miss leading 00 when it prints. use this instead.

其次,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();
}