簡體   English   中英

Java中字節數組的位移操作

[英]Bit shift operations on a byte array in Java

如何將字節數組向右移動 n 個位置? 例如,將 16 字節數組右移 29 個位置? 我在某處讀到它可以使用 long 來完成嗎? 會使用這樣的長期工作:

Long k1 = 從 0 到 7 的字節數組

Long k2 = 從 8 到 15 的字節數組

然后使用 Long.rotateRight(Long x, number of rotations) 將這兩個 long 向右旋轉。這兩個 long 將如何連接回字節數組?

我相信你可以使用 java.math.BigInteger 來做到這一點,它支持任意大數的移位。 這具有簡單的優點,但缺點是不填充原始字節數組大小,即輸入可能是 16 個字節,但輸出可能只有 10 個等等,需要額外的邏輯。

大整數方法

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

長時間操縱

我不知道為什么你想用 rotateRight() 來做這個,因為這會讓生活變得更加困難,你必須在 K1 等左側出現的位上空白。你最好使用 shift IMO 如下所述。 我使用了 20 的移位來被 4 整除,這樣更容易看到輸出中的半字節移動。

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) 將每個長 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

這是我想出的將字節數組左移任意數量的位的方法:

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

1.手動實現

這是不使用BigInteger (即不創建輸入數組的副本)和無符號右移( BigInteger當然只支持算術移位)的左右移位實現

左移<<

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

使用這個的這個項目

2. 使用 BigInteger

請注意, BigInteger內部將字節數組轉換為 int[] 數組,因此這可能不是最優化的解決方案:

算術左移<<:

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

算術右移>>:

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

3. 外部圖書館

使用字節 java 庫*:

添加到 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();

*完全免責聲明:我是開發人員。

這是一個舊帖子,但我想更新亞當的答案。 長解決方案只需進行一些調整即可。

為了旋轉,使用 >>> 而不是 >>,因為 >> 將填充有效位,改變原始值。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM