【C#】ビットを直感的に操作するクラスが無いので作る
2023.04.25
CATEGORY : プログラミング
あんみんどうふです。
C#はビット操作するクラスが標準搭載されていません。なんで?
ないものはないので自分で作ります。
Bitクラスの作成
クラス名はわかりやすくBitという名前にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class Bit { public List<bool> Bits { get; set; } public Bit() { Bits = new List<bool>(); } public Bit(int num) { Bits = new List<bool>(); for (int i = 0; i < 32; i++) Bits.Add(((num >> i) & 0x1) == 1); } } |
Bitsというリストにビットを持たせています。0と1だけならboolで表現可能なのでList<bool>型です。
最後のBitクラスに引数にint型の数値を入れると配列に変換するようにしています。
2進数を文字列で出力
1 2 3 4 5 6 7 8 | public string ToString(int pad = 0) { StringBuilder sb = new StringBuilder(); foreach (bool b in Enumerable.Reverse(Bits)) if (sb.Length == 0 && !b) continue; else sb.Append(b ? 1 : 0); return sb.ToString().PadLeft(pad, '0'); } |
Bitsをそのまま順に取得すると逆に並んでしまうので、Reverseで一旦逆に入れ替えてから取得しています。
引数padは0埋めする桁数を指定できます。
1 2 3 4 5 6 | int num = 123; Bit bit1 = new Bit(num); Console.WriteLine(bit1.ToString()); // 1111011 Console.WriteLine(bit1.ToString(8)); // 01111011 |
数値に変換 (+範囲指定)
1 2 3 4 5 6 7 8 | public int GetInt(int start = 0, int length = 0) { int res = 0; if (length == 0) length = Bits.Count; for (int b = 0; b < length; b++) res |= (Bits[start + b] ? 1 : 0) << b; return res; } |
Bitsリスト内の各ビットを1ビットずつ左にずらしながら足していくことで数値に変換しています。
startはビットの開始位置、lengthは開始位置から数えたビットの長さ(取得する量)。指定しない場合、Bits全体を変換します。
1 2 3 4 5 6 7 | Bit bit2 = new Bit(234); // 234 → 11101010 → 234 Console.WriteLine(bit2.GetInt()); // 234 // 234 → 11101010 → 2~4ビットを範囲指定 → 1010 → 10 Console.WriteLine(bit2.GetInt(2, 4)); // 10 |
ビットを直接変更
1 2 3 4 5 6 7 8 9 10 11 | public bool this[int index] { get { return Bits[index]; } set { Bits[index] = value; } } |
indexを指定することで直接値を取得・セットが可能です。
1 2 3 4 5 6 7 8 9 10 11 | Bit bit3 = new Bit(345); Console.WriteLine(bit3.GetInt()); // 345 Console.WriteLine(bit3.ToString()); // 101011001 // 0ビット目を取得 Console.WriteLine(bit3[0]); // True bit3[1] = true; // 1ビット目を0→1に変更 bit3[2] = true; // 2ビット目を0→1に変更 Console.WriteLine(bit3.GetInt()); // 351 Console.WriteLine(bit3.ToString()); // 101011111 |
Byteコレクションをビット化
1 2 3 4 5 6 7 | public Bit(IEnumerable<byte> arr) { Bits = new List<bool>(); foreach (byte val in arr) for (int i = 0; i < 8; i++) Bits.Add(((val >> i) & 0x1) == 1); } |
List、配列等のコレクションをビット化します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | byte[] arr = { 1, 10, 100, 200 }; Bit bit4 = new Bit(arr); Console.WriteLine(bit4.ToString(32)); // 11001000 01100100 00001010 00000001 // 16~24ビット目を取得 Console.WriteLine(bit4.GetInt(16, 8)); // 100 List<byte> list = new List<byte>() { 10, 20, 30, 40 }; Bit bit5 = new Bit(list); Console.WriteLine(bit5.ToString(32)); // 00101000 00011110 00010100 00001010 // 8~16ビット目を取得 Console.WriteLine(bit5.GetInt(8, 8)); // 20 |
ビットをbyte配列で取得
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public byte[] GetBytes(int start = 0, int length = 0) { if (length == 0) length = Bits.Count; byte[] res = new byte[(int)Math.Ceiling((double)length / 8)]; for (int i = 0; i < res.Length; i++) for (int b = 0; b < 8; b++) { int index = start + i * 8 + b; if (index < Bits.Count) res[i] |= (byte)((Bits[index] ? 1 : 0) << b); else res[i] |= 0; } return res; } |
開始位置(start)、長さ(length)を指定してbyte配列として取得します。指定が無い場合、Bits全体を変換します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | byte[] arr = { 11, 22, 33, 44 }; Bit bit6 = new Bit(arr); Console.WriteLine(bit6.ToString(32)); // 00101100 00100001 00010110 00001011 foreach (byte val in bit6.GetBytes()) { Console.WriteLine(val); } // 11 // 22 // 33 // 44 // 2~32ビット目まで取得 foreach (byte val in bit6.GetBytes(2, 30)) { Console.WriteLine(val); } // 130 // 69 // 8 // 11 |
int型の数値をセット
1 2 3 4 5 6 7 8 9 10 11 12 | public void SetInt(int start, int length, int value) { int bit = 0; byte[] val = BitConverter.GetBytes(value); for (int i = 0; i < val.Length; i++) for (int j = 0; j < 8; j++) { Bits[i * 8 + start + j] = ((val[i] >> j) & 1) == 1; bit += 1; if (bit >= length) return; } } |
開始位置(start)から長さ(length)の分だけ数値(value)をセットします。
1 2 3 4 5 6 | Bit bit7 = new Bit(130); Console.WriteLine(bit7.ToString(8)); // 10000010 int num = 62; // 62 = 00111110 bit7.SetInt(2, 6, num); // 現在の2ビット目から6ビット分までの範囲に62をセット Console.WriteLine(bit7.ToString(8)); // 11111010 |
C#、BitConverterぐらいじゃ全然物足りないのでもっと軽率にビット演算系標準搭載してもらって構わないです。
以上。