我从硬件设备接收二进制数据,其中包括位打包的7字节消息。例如,一个数据元素x取自跨越字节边界的位6-9。
因为它只有7个字节,所以我想创建一个UInt64可能会很方便,然后我可以使用二元运算符很容易地操作它:
//pseudo
void receiveData(IList<byte> bytes)
{
UInt64 data = ???(bytes,0,7);
var x = (bytes >> 6) & (0b1111);
...
}如何获取bytes -> data?显然,一种选择是使用循环,但是否已经有.Net应用程序接口来处理此问题?
(也欢迎使用其他方式完成此操作)
发布于 2021-06-04 23:02:08
听起来你想要[BitConverter.ToInt64(byte[] value, int startIndex)](http://msdn.microsoft.com/en-us/library/system.bitconverter.toint64(v=vs.110%29).
ToInt64方法将从索引startIndex到startIndex +7的字节转换为Int64值。数组中的字节顺序必须反映计算机系统体系结构的字节顺序;有关更多信息,请参见BitConverter类主题的备注部分。
发布于 2021-06-05 15:54:24
据我所知,没有内置库来完成这项任务,也没有第三方库。
如果只向内置方法BitConverter.ToInt64(byte[] value, int startIndex)传递7个字节,它将抛出一个System.ArgumentException: 'Destination array is not long enough.。
如果您希望使用this方法,您可以用一个8字节填充您的7个字节
static void receiveData(byte[] bytes)
{
var padded = new byte[8];
Array.Copy(bytes, padded, bytes.Length);
var data = BitConverter.ToUInt64(padded, 0);
var x = (data >> 6) & (0b1111);
//...
}如果效率是您的目标,那么在展开的循环中手动解压数据是可行的方法:
static void receiveData(byte[] bytes)
{
var data = (ulong)bytes[0] +
((ulong)bytes[1] << 8) +
((ulong)bytes[2] << 16) +
((ulong)bytes[3] << 24) +
((ulong)bytes[4] << 32) +
((ulong)bytes[5] << 40) +
((ulong)bytes[6] << 48);
var x = (data >> 6) & (0b1111);
//...
}在班轮生产线上有各种效率较低的方法来完成上述两项任务。
对于后一种情况,使用一行::
static void receiveData(byte[] bytes)
{
var data = (ulong)Enumerable.Range(0, bytes.Length)
.Sum(i=> ((long)bytes[i]) << (i * 8));
var x = (data >> 6) & (0b1111);
//...
}如CSharp中的各种网络代码所示,必须使用字节索引从每个字节中提取二进制级的原始位,然后使用掩码和移位操作,并且当数据的位索引跨越字节的字边界时,通常需要使用加法操作。
由于您无论如何都要执行这样的位操作,另一个备选方法是使用字段构建一个类,这样属性就可以通过索引单独访问字节。
public class MyClass
{
private byte[] data;
public MyClass(byte[] bytes)
{
//TODO check bytes is not null and is correct length
bytes = data;
}
public int X => (data[0] >> 6) & (0b1111);
public int Y => ((data[0] >> 6) & ~(0b1111)) + ((data[1] >> 2) & (0b11));
}如果您的数据只有7个字节,那么您可以首先将数据转换为uint64,然后使用二进制操作来提取您要查找的位。
如果数据超过8个字节,则可以使用System.Numerics.BigInteger,它可以使用Byte[]并使用二进制操作来构造
var big = new BigInteger(new byte[] { 1, 2, 3, 4, 6, 7, 8, 9, 10,11});
var shifted = big>>65; // skip 8 bytes
var wanted= shifted & 7; // take first 3 bit-packed values另一种选择是使用c/c++,它允许您使用联合结构,并将其传递回托管代码。
至于内置选项,您最后的选择是BitArray,它可以为您提供传递给构造函数的任意字节数组中的布尔值列表或数组,但这使您非常缺乏对这些布尔值的任何其他操作:
var b = new BitArray(bytes);
var j = b.Cast<Boolean>().ToList();发布于 2021-06-05 22:23:15
我考虑的一个选择是,使用LINQ的魔力:
UInt64 data = bytes.Take(7).Reverse().Aggregate(0ul, (result, next) => (result<<8) + next);老派的C程序员可能会对可能的低效感到不寒而栗,但我发现这很有趣。我必须添加Reverse(),因为我是小端的,并且我看不到在lambda中做result + next << (8*index的任何方法。
https://stackoverflow.com/questions/67839445
复制相似问题