我正在处理一个非常长的二进制字符串,其中包含"字段"可以是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位)。
或者有更好的方法吗?
编辑:添加了更多代码以使示例更易于理解。
答案 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,
}
如您所见,对于保存输入数据,我将快速预设Image
和ImageIndexed
放在那里组合多个位,以快速指示输入根本不重要,或者需要至少低至8位。