简体   繁体   English

如何从 C# 中具有偏移量和位大小的 byte[] 读取 int

[英]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;
}

C# 90 bytes C# 90 字节

    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使用BitArrayBitConverter的解决方案

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类(或其他技巧)读取整个intlong ,然后进行一些移位和屏蔽以从中提取目标位范围。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM