- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试构建一个实用程序类以使按位操作和转换更具可读性。目前,我一直致力于构建一种从字节数组中提取位并从中形成新的 byte[] 的方法。不用说,我对按位运算不太熟练。
我相信它可能可以使用 BitSet 来实现,但是会有太多转换,并且实现将是 Java 特定的。如果有一个清晰的算法,以后可以轻松移植到其他语言,那就太好了。
到目前为止我已经做到了:
public static byte[] toBytes(int offset /*full bytes*/, int bitsOffset /*bytes + bits*/, int bitsCount, byte... bytes) {
int bytesCount = bitsCount / 8;
int paddingBits = bitsCount % 8;
int partialBits = 8 - paddingBits;
if (paddingBits > 0) {
bytesCount++;
}
byte[] data = new byte[bytesCount];
return data;
}
我已将上述内容注释掉并临时替换为
public static byte[] toBytes(int offset, int bitsOffset, int bitsCount, byte... bytes) {
int firstBitIndex = (offset * 8) + bitsOffset;
return new BigInteger(new BigInteger(1, bytes).toString(2).substring(firstBitIndex, firstBitIndex + bitsCount), 2).toByteArray();
}
但是,我仍然希望有一个适当的实现,开销尽可能小,并且不特定于 Java(不使用特定于 Java 的工具,如 BitSet)
这是我期望它做什么的提示
/**
* [0000 0110 1111 0010] = toBytes(1, 4, 12, [xxxx xxxx xxxx 0110 1111 0010 xxxx xxxx])
* [0000 0110 1111 0010] = toBytes(1, 5, 12, [xxxx xxxx xxxx x011 0111 1001 0xxx xxxx])
* [0000 0110 1111 0010] = toBytes(1, 6, 12, [xxxx xxxx xxxx xx01 1011 1100 10xx xxxx])
*/
这里是一些单元测试
public class ByteUtilTest {
@Test
public void toBytes_sameByte() {
byte[] result = ByteUtil.toBytes(1, 4, 3,
toByte("11111111"),
toByte("11110111"),
toByte("11111111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000011")), toBinaryString(result));
}
@Test
public void toBytes_sameByte_full() {
byte[] result = ByteUtil.toBytes(1, 0, 8,
toByte("11111111"),
toByte("01110101"),
toByte("11111111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("01110101")), toBinaryString(result));
}
@Test
public void toBytes_sameByte_noneWithoutOffset() {
byte[] result = ByteUtil.toBytes(1, 0, 0,
toByte("11111111"),
toByte("01110101"),
toByte("11111111"),
toByte("11111111"));
assertEquals(0, result.length);
}
@Test
public void toBytes_sameByte_noneWithOffset() {
byte[] result = ByteUtil.toBytes(1, 3, 0,
toByte("11111111"),
toByte("01110101"),
toByte("11111111"),
toByte("11111111"));
assertEquals(0, result.length);
}
@Test
public void toBytes_twoBytes_resultWithTwoBytes() {
byte[] result = ByteUtil.toBytes(1, 2, 11,
toByte("11111111"),
toByte("01110101"),
toByte("10011111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("10110011")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_resultWithOneByte() {
byte[] result = ByteUtil.toBytes(1, 2, 7,
toByte("11111111"),
toByte("01110101"),
toByte("10011111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("01101011")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_firstFull() {
byte[] result = ByteUtil.toBytes(1, 0, 11,
toByte("11111111"),
toByte("01110101"),
toByte("10011111"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000011"), toByte("10101100")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_lastFull() {
byte[] result = ByteUtil.toBytes(1, 5, 11,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000101"), toByte("10011101")), toBinaryString(result));
}
@Test
public void toBytes_twoBytes_bothFull() {
byte[] result = ByteUtil.toBytes(1, 0, 16,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("01110101"), toByte("10011101")), toBinaryString(result));
}
@Test
public void toBytes_threeBytes() {
byte[] result = ByteUtil.toBytes(1, 2, 19,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("00000110"),
toByte("10110011"),
toByte("10110111")),
toBinaryString(result));
}
@Test
public void toBytes_threeBytes_firstFull() {
byte[] result = ByteUtil.toBytes(1, 0, 19,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("00000011"),
toByte("10101100"),
toByte("11101101")),
toBinaryString(result));
}
@Test
public void toBytes_threeBytes_lastFull() {
byte[] result = ByteUtil.toBytes(1, 2, 22,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("00110101"),
toByte("10011101"),
toByte("10111111")),
toBinaryString(result));
}
@Test
public void toBytes_threeBytes_allFull() {
byte[] result = ByteUtil.toBytes(1, 0, 24,
toByte("11111111"),
toByte("01110101"),
toByte("10011101"),
toByte("10111111"));
assertEquals(
toBinaryString(
toByte("01110101"),
toByte("10011101"),
toByte("10111111")),
toBinaryString(result));
}
@Test
public void toBytes_bitsOffset_4() {
byte[] result = ByteUtil.toBytes(1, 4, 12,
toByte("11111111"),
toByte("11110110"),
toByte("11110010"),
toByte("11111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("11110010")), toBinaryString(result));
}
@Test
public void toBytes_bitsOffset_5() {
byte[] result = ByteUtil.toBytes(1, 5, 12,
toByte("11111111"),
toByte("11111011"),
toByte("01111001"),
toByte("01111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("11110010")), toBinaryString(result));
}
@Test
public void toBytes_bitsOffset_6() {
byte[] result = ByteUtil.toBytes(1, 6, 12,
toByte("11111111"),
toByte("11111101"),
toByte("10111100"),
toByte("10111111"));
assertEquals(toBinaryString(toByte("00000110"), toByte("11110010")), toBinaryString(result));
}
private String toBinaryString(byte... data) {
StringBuilder binaryStr = new StringBuilder();
String value = Integer.toBinaryString(data[0]);
if (value.length() > 8) value = value.substring(value.length() - 8);
else if (value.length() < 8) value = String.format("%8s", value).replace(" ", "0");
binaryStr.append(value);
for (int i = 1; i < data.length; i++) {
value = Integer.toBinaryString(data[i]);
if (value.length() > 8) value = value.substring(value.length() - 8);
else if (value.length() < 8) value = String.format("%8s", value).replace(" ", "0");
binaryStr.append(" ").append(value);
}
return binaryStr.toString();
}
private String toString(byte[] data) {
return Arrays.toString(data);
}
private byte toByte(String binary) {
return (byte) Integer.parseInt(binary, 2);
}
}
最佳答案
So far I've got [...]
byte[] data = new byte[bytesCount];
不幸的是,这种方法仅在您的位偏移量是 8 的倍数时才有效。在所有其他情况下,您必须划分要复制的每个字节。下图说明了如何划分每个字节以及将划分后的部分放置在何处。
MSB = 最高有效位
LSB = 最低有效位
由于存在很多极端情况,实现上述算法有点棘手。以下实现通过了您和我的所有测试。我使用了许多变量来给所有计算赋予有意义的名称,希望更容易理解。您可以通过消除其中一些变量并就地计算一些值来缩短实现时间。
我冒昧地将您的函数 toBytes
重命名为 bitSubstring
。对于已经将字节作为输入的方法来说,以前的名称 toBytes
似乎有点不合适。
public static byte[] bitSubstring(int byteOffset, int bitOffset,
int lengthInBits, byte... source) {
return bitSubstring(8 * byteOffset + bitOffset, lengthInBits, source);
}
public static byte[] bitSubstring(int startBit, int lengthInBits,
byte... source) {
assert startBit >= 0 && startBit < 8 * source.length;
assert lengthInBits >= 0 && startBit + lengthInBits <= 8 * source.length;
int lengthInBytes = (int) Math.ceil(lengthInBits / 8.0);
byte[] target = new byte[lengthInBytes];
int startByte = startBit / 8;
int endBitExclusive = startBit + lengthInBits;
int endByteExclusive = (int) Math.ceil(endBitExclusive / 8.0);
int sourceBytesToRead = endByteExclusive - startByte;
int lowerPartSize = 8 * endByteExclusive - endBitExclusive;
int shiftLowerUp = (8 - lowerPartSize);
int shiftUpperDown = lowerPartSize;
int lastSrc = 0;
if (sourceBytesToRead > lengthInBytes) {
lastSrc = source[startByte] & 0xFF;
startByte++;
}
for (int targetByte = 0; targetByte < target.length; ++targetByte) {
int curSrc = source[startByte + targetByte] & 0xFF;
target[targetByte] |= (lastSrc << shiftLowerUp)
| (curSrc >>> shiftUpperDown);
lastSrc = curSrc;
}
int overhang = 8 * lengthInBytes - lengthInBits;
if (overhang > 0) {
target[0] &= 0xFF >>> overhang;
}
return target;
}
上面的算法应该相当快。但是,如果您只对实现大小和可读性感兴趣,那么逐点复制的方法会更好。
public static byte[] bitSubstringSlow(int startBitSource, int lengthInBits,
byte... source) {
byte[] target = new byte[(int) Math.ceil(lengthInBits / 8.0)];
int startBitTarget = (8 - lengthInBits % 8) % 8;
for (int i = 0; i < lengthInBits; ++i) {
setBit(target, startBitTarget + i, getBit(source, startBitSource + i));
}
return target;
}
public static int getBit(byte[] source, int bitIdx) {
return (source[bitIdx / 8] >>> (7 - bitIdx % 8)) & 1;
}
public static void setBit(byte[] target, int bitIdx, int bitValue) {
int block = bitIdx / 8;
int shift = 7 - bitIdx % 8;
target[block] &= ~(1 << shift);
target[block] |= bitValue << shift;
}
...或者可重用性较差但更短:
public static byte[] bitSubstringSlow2(int startBitSource, int lengthInBits,
byte... source) {
byte[] target = new byte[(int) Math.ceil(lengthInBits / 8.0)];
int startBitTarget = (8 - lengthInBits % 8) % 8;
for (int i = 0; i < lengthInBits; ++i) {
int srcIdx = startBitSource + i;
int tgtIdx = startBitTarget + i;
target[tgtIdx / 8] |= ((source[srcIdx / 8] >>> (7 - srcIdx % 8)) & 1)
<< (7 - tgtIdx % 8);
}
return target;
}
关于java - 实用程序将字节数组中的位提取到新的字节[]中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56998536/
我正在尝试创建一个包含 int[][] 项的数组 即 int version0Indexes[][4] = { {1,2,3,4}, {5,6,7,8} }; int version1Indexes[
我有一个整数数组: private int array[]; 如果我还有一个名为 add 的方法,那么以下有什么区别: public void add(int value) { array[va
当您尝试在 JavaScript 中将一个数组添加到另一个数组时,它会将其转换为一个字符串。通常,当以另一种语言执行此操作时,列表会合并。 JavaScript [1, 2] + [3, 4] = "
根据我正在阅读的教程,如果您想创建一个包含 5 列和 3 行的表格来表示这样的数据... 45 4 34 99 56 3 23 99 43 2 1 1 0 43 67 ...它说你可以使用下
我通常使用 python 编写脚本/程序,但最近开始使用 JavaScript 进行编程,并且在使用数组时遇到了一些问题。 在 python 中,当我创建一个数组并使用 for x in y 时,我得
我有一个这样的数组: temp = [ 'data1', ['data1_a','data1_b'], ['data2_a','data2_b','data2_c'] ]; // 我想使用 toStr
rent_property (table name) id fullName propertyName 1 A House Name1 2 B
这个问题在这里已经有了答案: 关闭13年前。 Possible Duplicate: In C arrays why is this true? a[5] == 5[a] array[index] 和
使用 Excel 2013。经过多年的寻找和适应,我的第一篇文章。 我正在尝试将当前 App 用户(即“John Smith”)与他的电子邮件地址“jsmith@work.com”进行匹配。 使用两个
当仅在一个边距上操作时,apply 似乎不会重新组装 3D 数组。考虑: arr 1),但对我来说仍然很奇怪,如果一个函数返回一个具有尺寸的对象,那么它们基本上会被忽略。 最佳答案 这是一个不太理
我有一个包含 GPS 坐标的 MySQL 数据库。这是我检索坐标的部分 PHP 代码; $sql = "SELECT lat, lon FROM gps_data"; $stmt=$db->query
我需要找到一种方法来执行这个操作,我有一个形状数组 [批量大小, 150, 1] 代表 batch_size 整数序列,每个序列有 150 个元素长,但在每个序列中都有很多添加的零,以使所有序列具有相
我必须通过 url 中的 json 获取文本。 层次结构如下: 对象>数组>对象>数组>对象。 我想用这段代码获取文本。但是我收到错误 :org.json.JSONException: No valu
enter code here- (void)viewDidLoad { NSMutableArray *imageViewArray= [[NSMutableArray alloc] init];
知道如何对二维字符串数组执行修剪操作,例如使用 Java 流 API 进行 3x3 并将其收集回相同维度的 3x3 数组? 重点是避免使用显式的 for 循环。 当前的解决方案只是简单地执行一个 fo
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
我有来自 ASP.NET Web 服务的以下 XML 输出: 1710 1711 1712 1713
如果我有一个对象todo作为您状态的一部分,并且该对象包含数组列表,则列表内部有对象,在这些对象内部还有另一个数组listItems。如何更新数组 listItems 中 id 为“poi098”的对
我想将最大长度为 8 的 bool 数组打包成一个字节,通过网络发送它,然后将其解压回 bool 数组。已经在这里尝试了一些解决方案,但没有用。我正在使用单声道。 我制作了 BitArray,然后尝试
我们的数据库中有这个字段指示一周中的每一天的真/假标志,如下所示:'1111110' 我需要将此值转换为 boolean 数组。 为此,我编写了以下代码: char[] freqs = weekday
我是一名优秀的程序员,十分优秀!