在處理數字有許多位時,可以使用C#中的BitArray類和BitVector32結構。BitArray類與BitVector32結構的區別是:BitArray類可以重新設置大小,對於事先不知道需要的位數,可以使用BitArray類,它可以包含很多位。BitVector32結構是基於棧的,相對於類比較快,但是它只含有32位,存儲在一個整數中。
1、BitArray類
BitArray類是一個引用類型,它包含一個int數組,每32位使用一個新整數。每一個位都是一個bool值。
創建一個位數組:
//*****************創建位數組***********************
BitArray bits = new BitArray(8);//創建一個包含8位的數組 bits.SetAll(true);//將8個位都設置為true bits.Set(1, false);//將下標為1(第二個位)設置為false bits[5] = false;//將下標為5的位設置為false Console.WriteLine("Initialized: "); foreach (bool b in bits) { Console.WriteLine(b ? 1 : 0); }
如果創建一個位數組時,在構造函數中傳入一個位數組,那么新創建的位數組將和參數位數組具有相同的值。在構造位數組時,可以使用字節數組、int類型數組、bool數組和位數組進行初始化構造位數組。注意:只能是數組形式。如果傳入一個int類型的數字,則認為創建int長度的位的位數組。
位數組中有許多方法,如:Set()設置某個位上的值;SetAll()設置所有位上的值;Not()對所有位取反;And()、Or()、Xor()用於兩個位數組之間的合並運算等方法。
2、BitVector32結構
如果事先知道需要的位數,可以使用BitVector32結構代替BitArray類。因為BitVector32是值類型的關系,所以在效率上相對較高。對於需要更多的位可以使用多個BitVector32結構或者BitArray類。
使用默認構造函數創建一個BitVector32結構,所有位都默認為false。然后創建掩碼,以訪問位矢量中的位。使用索引器訪問對應的位,並設置相應的字段。
BitVector32 bits1 = new BitVector32(); int bit1 = BitVector32.CreateMask(); //創建掩碼,運行后bit1=1; int bit2 = BitVector32.CreateMask(bit1);//創建掩碼,運行后bit2=2; int bit3 = BitVector32.CreateMask(bit2);//創建掩碼,運行后bit3=3; int bit4 = BitVector32.CreateMask(bit3);//創建掩碼,運行后bit4=4; int bit5 = BitVector32.CreateMask(bit4);//創建掩碼,運行后bit5=5; bits1[bit1] = true; bits1[bit2] = true; bits1[bit3] = false; bits1[bit4] = true; bits1[bit5] = true; Console.WriteLine(bits1);//輸出為:BitVector32{00000000000000000000000000011011}
也可以自己創建掩碼,一次性設置多個位。例如十六進制的abcdef與二進制的1010 1011 1100 1101 1110 1111相同。因此對於一個新創建的bits:
bits[0xabcdef] = true;//輸出為:BitVector32{00000000101010111100110111101111}
IPv4中的應用:IPv4地址定義為一個4字節的數,該數存儲在一個整數中。可以定義4個片段,把這個整數拆分開。在多播IP消息中,使用了幾個32位的值。其中一個32位的值放在這些片段中:16位表示源號,8位表示查詢器的查詢內部碼,3位表示查詢器的健壯變量,1位表示抑制標志,保留4位。也可以自己定義位含義,以節省內存。下面的例子中,模擬接收到值0x79abcdef:
int received = 0x79abcdef; BitVector32 bits = new BitVector32(received);//bits結構為:BitVector32{01111001101010111100110111101111}
接着創建6個片段:第一個片段A需要12位,由十六進制值0xffff定義;第二個片段B需要8位;第三個片段C需要4位;第四個片段D和第五個片段E需要3位;第六個片段需要2位。除第一個片段外,其余片段皆將上一個片段作為參數傳遞,使得該片段從上一片段的結尾處開始。CreateSection()方法返回一個BitVector32.Section類型的值,該類型包含了該片段的偏移量和掩碼:
//sections: FF EEE DDD CCCC BBBBBBBB AAAAAAAAAAAA BitVector32.Section sectionA = BitVector32.CreateSection(0xfff);
BitVector32.Section sectionB= BitVector32.CreateSection(0xff, sectionA);
BitVector32.Section sectionC = BitVector32.CreateSection(0xf, sectionB);
BitVector32.Section sectionD = BitVector32.CreateSection(0x7, sectionC);
BitVector32.Section sectionE = BitVector32.CreateSection(0x7, sectionD);
BitVector32.Section sectionF = BitVector32.CreateSection(0x3, sectionE);
把一個BitVector32.Section類型的值傳遞給BitVector32結構的索引器,會返回一個int,它映射到位矢量的片段上。
首先創建一個展示方法IntToBInaryString():
static string IntToBinaryString(int bits,bool removeTrailingZero) { StringBuilder builder = new StringBuilder(32); for(int i=0;i<32;i++) { if ((bits & 0x80000000) != 0) { builder.Append("1"); } else { builder.Append("0"); } bits=bits << 1; } string s = builder.ToString(); if(removeTrailingZero) { return s.TrimStart('0'); } return s; }
結果顯示了片段A~F的位表示:
Console.WriteLine("Section A: {0}", IntToBinaryString(bits[sectionA], true));//輸出為:Section A: 110111101111 Console.WriteLine("Section B: {0}", IntToBinaryString(bits[sectionB], true));//輸出為: Section B: 10111100 Console.WriteLine("Section C: {0}", IntToBinaryString(bits[sectionC], true));//輸出為:Section C: 1010 Console.WriteLine("Section D: {0}", IntToBinaryString(bits[sectionD], true));//輸出為:Section D: 1 Console.WriteLine("Section E: {0}", IntToBinaryString(bits[sectionE], true));//輸出為:Section E: 111 Console.WriteLine("Section F: {0}", IntToBinaryString(bits[sectionF], true));//輸出為:Section F: 1
以上顯示結果,與最開始的整數位一一對應:
//BitVector32{ 01 111 001 1010 10111100 110111101111} //sections: FF EEE DDD CCCC BBBBBBBB AAAAAAAAAAAA
3、BitArray與Byte之間的轉換
/// <summary> /// 通過位構建字節 /// </summary> /// <param name="boolArray">bool位值</param> /// <returns>字節數組</returns> byte[] ConvertToByte(bool[] boolArray) { int num = (int)Math.Ceiling((boolArray.Length / 8.0)); //向上取整 bool[] newBool = new bool[num * 8]; boolArray.CopyTo(newBool, 0); //補位 BitArray bits = new BitArray(newBool); byte[] bytes = new byte[num]; bits.CopyTo(bytes, 0); return bytes; } bool[] bits = new bool[] { true, false, false, false, false, false, true, true, }; //將其轉換為位值 byte[] myByte = ConvertToByte(bits); //獲取每個位上的值:false/true BitArray recoveredBit = new BitArray( myByte );