[英]How to read an int from a byte[] with an offset and a size of bits in C#
I need a function that takes as parameters an offset and a size of bits to read an int value from a byte array.我需要一个将偏移量和位大小作为参数的函数,以从字节数组中读取 int 值。
int GetInt(byte[] data, int bitOffset, int bitSize)
For example, I have the following array of bytes:例如,我有以下字节数组:
66 DC 00 00 6A DC 00 00
66 DC 00 00 58 DC 00 00
54 DC 00 00 50 DC 00 00
4C DC 00 00 00 00 00 00
00 00 00 00 00 00 00 08
F0 FF FF 9F F4 7F 20 9A
91 EB 85 88 3F 6E 00 80
3D 6E 00 80 3B 6E 00 00
Same in bits:位相同:
01100110 00111011 00000000 00000000 01010110 00111011 00000000 00000000
01100110 00111011 00000000 00000000 00011010 00111011 00000000 00000000
00101010 00111011 00000000 00000000 00001010 00111011 00000000 00000000
00110010 00111011 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00010000
00001111 11111111 11111111 11111001 00101111 11111110 00000100 01011001
10001001 11010111 10100001 00010001 11111100 01110110 00000000 00000001
10111100 01110110 00000000 00000001 11011100 01110110 00000000 00000000
How do I most efficiently ensure that the following function has these return values?:如何最有效地确保以下函数具有这些返回值?:
var a = GetInt(data, 0, 32); // a = 56422
var b = GetInt(data, 313, 11); // b = 4
EDIT: Here the bytes as an C# Array:编辑:这里的字节作为 C# 数组:
new byte[] { 0x66, 0xDC, 0x00, 0x00, 0x6A, 0xDC, 0x00, 0x00, 0x66, 0xDC, 0x00, 0x00, 0x58, 0xDC, 0x00, 0x00, 0x54, 0xDC, 0x00, 0x00, 0x50, 0xDC, 0x00, 0x00, 0x4C, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xF0, 0xFF, 0xFF, 0x9F, 0xF4, 0x7F, 0x20, 0x9A, 0x91, 0xEB, 0x85, 0x88, 0x3F, 0x6E, 0x00, 0x80, 0x3D, 0x6E, 0x00, 0x80, 0x3B, 0x6E, 0x00, 0x00 }
EDIT 2: I have already implemented my own solution as well, with which i could take all the values for this post here.编辑 2:我也已经实现了自己的解决方案,我可以在这里获取这篇文章的所有值。 Im just very unhappy with my solution because i dont want to pass the array into a BitArray every time.我对我的解决方案非常不满意,因为我不想每次都将数组传递给 BitArray。 To read a file, this function is called several hundred thousand times.要读取一个文件,这个函数会被调用几十万次。
public static int GetdInt(this byte[] data, int bitOffset, int bitSize)
{
var bits = new BitArray(data);
var output = 0;
for(var bitIndex = 0; bitIndex < bitSize; bitIndex++)
{
var bit = bits.Get(bitOffset + bitIndex) ? 1 : 0;
output |= bit << bitIndex;
}
return output;
}
int GetInt(byte[] d,int o,int b)=>Enumerable.Range(o,b).Sum(i=>(d[i/8]>>i%8)%2>0?1<<i-o:0);
I've written this in code golfed form to poke fun at the fact that your question reads like code golf, and that you haven't shown any attempt of your own ;)我用代码打高尔夫球的形式写了这个来取笑你的问题读起来像打高尔夫球,而且你没有表现出你自己的任何尝试;)
Here is a unit test for any future answerers.这是任何未来回答者的单元测试。 (OP this might be helpful to you, too): (OP,这也可能对您有所帮助):
[TestMethod]
public void Test()
{
var bytes = new byte[]
{
0x66, 0xDC, 0x00, 0x00, 0x6A, 0xDC, 0x00, 0x00,
0x66, 0xDC, 0x00, 0x00, 0x58, 0xDC, 0x00, 0x00,
0x54, 0xDC, 0x00, 0x00, 0x50, 0xDC, 0x00, 0x00,
0x4C, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0xF0, 0xFF, 0xFF, 0x9F, 0xF4, 0x7F, 0x20, 0x9A,
0x91, 0xEB, 0x85, 0x88, 0x3F, 0x6E, 0x00, 0x80,
0x3D, 0x6E, 0x00, 0x80, 0x3B, 0x6E, 0x00, 0x00
};
RunTest(0, 32, 56422);
RunTest(313, 11, 4);
void RunTest(int offset, int bitSize, int expected)
{
var actual = GetInt(bytes, offset, bitSize);
Assert.AreEqual(actual, expected);
}
}
Edit: Since you showed your own attempt here is a non code-golfed answer:编辑:由于您在这里展示了自己的尝试,因此这是一个非代码高尔夫球的答案:
//first write a function that gets a bit value from the byte[]
bool GetBitFromByteArray(byte[] data, int bitNumber)
{
//8 bits per byte.
const int sizeOfByte = 8;
var byteNumber = bitNumber / sizeOfByte;//index within the byte array. Integer division always rounds down
var bitNumberWithinTheByte = bitNumber % sizeOfByte;//bit index within that byte
//now write a function that gets a bit value from a byte
return GetBitFromByte(data[byteNumber], bitNumberWithinTheByte);
}
bool GetBitFromByte(byte byteValue, int bitNumber)
{
//bit shift so that the bit in question is in the least significant place
var shifted = byteValue >> bitNumber;
//mod 2 checks if the least significant bit is 0 or 1
return shifted % 2 > 0;
}
int GetInt(byte[] data, int offset, int bitCount)
{
//get bit values in order
var bitValues = new List<bool>(bitCount);
for (int i = 0; i < bitCount; i++)
{
bitValues.Add(GetBitFromByteArray(data, i + offset));
}
//sum up the bit values as powers of 2
var intValue = 0;
for (int i = 0; i < bitCount; i++)
{
var bitValue = bitValues[i];
if (bitValue) intValue += 1 << i;//1<<i is equivalent to 2^i
}
return intValue;
}
If you are worried about performance and array allocation, the code golfed answer will actually be better.如果您担心性能和数组分配,那么代码打高尔夫球的答案实际上会更好。
Solution using BitArray andBitConverter使用BitArray和BitConverter的解决方案
int GetInt(byte[] data, int bitOffset, int bitSize)
{
var bits = new BitArray(data).RightShift(bitOffset);
bits.Length = bitSize;
var bytes = new byte[4];
bits.CopyTo(bytes, 0);
return BitConverter.ToInt32(bytes);
}
Here is one possible strategy, reading the bytes one-by-one until enough bits have been read, and then discarding any excess.这是一种可能的策略,一个接一个地读取字节,直到读取了足够的位,然后丢弃任何多余的位。 Passed the two test cases, but that's not sufficient testing IMO, do some more.通过了两个测试用例,但这还不够测试 IMO,多做一些。
static int GetInt(byte[] data, int bitOffset, int bitSize)
{
// first chunk is special, lower bits are discarded immediately
// to prevent trying to put more than 32 bits in `result`
// when `bitSize == 32` and `bitOffset != 0`
int byteOffset = bitOffset >> 3;
int result = data[byteOffset] >> (bitOffset & 7);
int resultOffset = 8 - (bitOffset & 7);
// the "rest", whole bytes are read from data and put into their place in the result
while (resultOffset < bitSize)
{
byteOffset++;
result |= data[byteOffset] << resultOffset;
resultOffset += 8;
}
// in general too many bits have been read at this point, discard excess
return result & (int)(uint.MaxValue >> -bitSize);
}
There are other possible strategies.还有其他可能的策略。 For example, reading a whole int
or long
with the BitConverter
class (or other tricks) and then doing some shifting and masking to extract the target range of bits from that.例如,使用BitConverter
类(或其他技巧)读取整个int
或long
,然后进行一些移位和屏蔽以从中提取目标位范围。 That has the potential to be more efficient, but has some unfortunate details to take care of when the target range doesn't cross the end of the array but the byte-aligned integer that contains them would.这有可能提高效率,但是当目标范围没有越过数组的末尾但包含它们的字节对齐整数会时,有一些不幸的细节需要处理。
The way the excess bits are discarded at the end doesn't work properly when bitSize = 0
, which I did not think is interesting to support, but could be done easily by handling that as a special case.当bitSize = 0
时,最后丢弃多余位的方式无法正常工作,我认为支持这一点并不有趣,但可以通过将其作为特殊情况处理来轻松完成。 Unlike (1 << bitSize) - 1
, the way I used does work for 32 bits.与(1 << bitSize) - 1
不同,我使用的方式确实适用于 32 位。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.