Java 中字节数组的移位操作
Bit shift operations on a byte array in Java
如何将字节数组向右移动 n 个位置?例如将一个 16 字节的数组右移 29 个位置?我在某处读到它可以用很长时间来完成?会使用这样的长篇作品吗:
long k1 = 0到7的字节数组
long k2 = 8到15的字节数组
然后使用Long.rotateRight(Long x, number of rotations)将这两个long向右旋转。如何将这两个long连接回一个字节数组?
我相信您可以使用 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
长时间操纵
我不知道你为什么要像 rotateRight() 那样做,因为这让生活变得更加困难,你必须在 K1 等左侧出现的位空白。你会如下所述,使用 shift IMO 会更好。我使用了可被 4 整除的 20 的移位,这样更容易看到半字节在输出中的移动。
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 中确定 "got pushed off the edge"
的位
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;
}
这是旧的 post,但我想更新 Adam 的回答。
长解决方案需要一些调整。
为了旋转,使用>>>代替>>,因为>>会填充有效位,改变原始值。
其次,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();
}
1。手动实施
这里是不使用BigInteger
(即不创建输入数组的副本)和无符号右移(BigInteger
当然只支持算术移位)的左移和右移实现
左移 <<
/**
* Left 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。外部图书馆
使用 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();
*完全免责声明:我是开发者。
如何将字节数组向右移动 n 个位置?例如将一个 16 字节的数组右移 29 个位置?我在某处读到它可以用很长时间来完成?会使用这样的长篇作品吗:
long k1 = 0到7的字节数组
long k2 = 8到15的字节数组
然后使用Long.rotateRight(Long x, number of rotations)将这两个long向右旋转。如何将这两个long连接回一个字节数组?
我相信您可以使用 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
长时间操纵
我不知道你为什么要像 rotateRight() 那样做,因为这让生活变得更加困难,你必须在 K1 等左侧出现的位空白。你会如下所述,使用 shift IMO 会更好。我使用了可被 4 整除的 20 的移位,这样更容易看到半字节在输出中的移动。
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 中确定 "got pushed off the edge"
的位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;
}
这是旧的 post,但我想更新 Adam 的回答。 长解决方案需要一些调整。
为了旋转,使用>>>代替>>,因为>>会填充有效位,改变原始值。
其次,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();
}
1。手动实施
这里是不使用BigInteger
(即不创建输入数组的副本)和无符号右移(BigInteger
当然只支持算术移位)的左移和右移实现
左移 <<
/**
* Left 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。外部图书馆
使用 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();
*完全免责声明:我是开发者。