以编程方式在多个字节上为相关位创建位掩码

时间:2018-05-06 06:35:55

标签: c# bit bitmask

我正在处理一个非常长的二进制字符串,其中包含"字段"可以是2到32位长的所有内容。因此,在某些情况下,"字段"可以跨越多个不均匀的字节。

我设法读出所有数据,但现在我正在编辑和切换数据。这是一个12位字段的当前工作示例,从整个字节开始:

public class CardSectionProperty
{
    public CardSectionProperty(CardDataContainer data, MapItem mapItem)
    {
        Data = data;
        Start = mapItem.Position;
        Length = mapItem.Length;
    }

    public CardDataContainer Data { get; } // A simple container for the binary string of the whole card.

    public int Start { get; }

    public int Length { get; }

    public void SetValue(long value)
    {
        var relevantBits= 0xFF0F; // <- I think this mask need to change depending on the field size and position in the binary string.
        var newValue = (int)value;
        var oldValue = (int)GetValue();
        var bitsToKeep = (~relevantBits) & oldValue;
        var newValueCorrected = Switch16BitEndianness(newValue) | bitsToKeep;
        SetValue(Convert.ToString(newValueCorrected, 2));
    }

    private void SetValue(string binaryValue)
    {
        var builder = new StringBuilder(Data.BinaryData);
        builder.Remove(Start, binaryValue.Length);
        builder.Insert(Start, binaryValue);
        Data.BinaryData = builder.ToString();
    }

    private int Switch16BitEndianness(int x)
    {
        return ((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8);
    }
}

这里的掩码是0xFF0F(1111 1111 0000 1111),这很容易理解,因为我们关心12位。但是以编程方式创建此掩码的最简单方法是什么?正如我所说,它可以是非常随机的,有时我们可以跨越三个字节(首先使用2位,然后使用8位,然后再使用最后一位的2位)。

或者有更好的方法吗?

编辑:添加了更多代码以使示例更易于理解。

2 个答案:

答案 0 :(得分:1)

您可以使用此代码:

private static uint CreateBitMask( int start, int length )
{
     uint mask = 0xffffffff;
     mask >>= 32 - length;
     mask <<= start;
     return mask;
}

这会创建一个'length'模式,从'start'位开始,其中bit 0是最不重要的(如果你在纸上写这些位,则写在右端)。

如果您需要更复杂的模式,您可以通过多次调用来CreatePattern

uint bits = CreateBitMask( 2, 3 );
bits |= CreateBitMask( 3, 8 );

答案 1 :(得分:1)

这里的主要问题实际上是...... 那些位字段?从您的问题来看,您的数据来自何处及其含义并不十分清楚。

通常,请参见这些位将分别表示要启用和禁用的特定布尔值,因此它们每个都应具有特定的名称。表示它的正常方法是使用[Flags]枚举。它们允许您构建值,其中每个位标志是您启用的命名选项。在.Net框架中已经使用了大量这些。

基本上,标志枚举是一个枚举,定义为包含可以组合的位。您只需为您使用的每个位指定一个名称,并将它们与OR运算组合。

[Flags]
public enum BitMask
{
    Empty = 0x00,    // 0000 0000 0000 0000
    BitOne = 0x01,   // 0000 0000 0000 0001
    BitTwo = 0x02,   // 0000 0000 0000 0010
    BitThree = 0x04, // 0000 0000 0000 0100
    BitFour = 0x08,  // 0000 0000 0000 1000
    BitFive = 0x10,  // 0000 0000 0001 0000
    BitSix = 0x20,   // 0000 0000 0010 0000
    BitSeven = 0x40, // 0000 0000 0100 0000
    BitEight = 0x80, // 0000 0000 1000 0000
    // etc...
}

您甚至可以在其中放置常用的预设,例如BitsFourAndFive = 0x18,这非常方便。当然,您的实际选项应该包含它们实际代表的布尔值。

然后你可以简单地用

组合你的位
BitMask mask = BitMask.BitTwo | BitMask.BitFive | BitMask.BitEight;

我假设您知道这些,因为您在代码中使用它们,但无论如何......要从掩码中删除一点,对反转值使用AND运算:

mask &= ~BitMask.BitTwo;

...并检查掩码中是否有位:

Boolean isTwo = (mask & BitMask.BitTwo) != 0;

最后,要获得实际价值......只需将其转换为整数。

Int32 maskvalue = (Int32)mask;

显然,反过来也可以这样做;如果您从输入中读取了值,则可以将其强制转换为BitMask。读取值后,可能会“启用第二位”检查:

Int32 readValue = ReadValue(); // wherever this comes from
Boolean isTwo = ((BitMask)readValue & BitMask.BitTwo) != 0;

或者,您可以将BitMask枚举条目转换为Int32;这也有效。

这是我的一个项目中的一个真实示例,它被一堆游戏精灵类用于暴露它们包含的图像类型,以及它们可以用作输入的哪种图像以保存为精灵类型:< / p>

[Flags]
public enum FileClass
{
    None = 0x00,
    Image1Bit = 0x01,
    Image4Bit = 0x02,
    Image8Bit = 0x04,
    ImageIndexed = 0x07,
    ImageHiCol = 0x08,
    Image = 0x0F,
}

如您所见,对于保存输入数据,我将快速预设ImageImageIndexed放在那里组合多个位,以快速指示输入根本不重要,或者需要至少低至8位。