Extracting data packet out of byte buffer - c#
I have a buffer of length 256 that receives byte sequences from bluetooth. The actual packet that I need to extract is starting and ending with byte 126. I want to extract the latest packet in the buffer using LINQ.
What I am doing now is checking for last index of 126 and then count backward until I reach another 126. There are some pitfalls as well, for example, two adjacent packet can result in two bytes of 126 next to eachother.
Here is a sample of buffer:
126 6 0 5 232 125 93 126 126 69 0
0 1 0 2 2 34 6 0 5 232 125
93 126 126 69 0 0 1 0 2 2 34
6 0 5 232 125 93 126 126 69 0 0
1 0 2 2 34 6 0 5 232 125 93
126 126 69 0 0
So the information I have is:
Packet starts and ends with byte value of 126
the next byte after the starting index does have value of 69
the 3 last byte right befor the ending byte of 126 is a CRC of the whole packet that I know how to calculate, so after extracting a packet I can check this CRC to see if I have the right packet
So at the end I want to have an array or list that contains the correct packet. for example:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
Can you give me a fast soloution of extracting this packet from buffer?
This is what I'v tried so far....it fails as it cant really return the correct packet I am looking for:
var data = ((byte[])msg.Obj).ToList(); //data is the buffer
byte del = 126; //delimeter or start/end byte
var lastIndex = data.LastIndexOf(del);
var startIndex = 0;
List<byte> tos = new List<byte>(); //a new list to store the result (packet)
//try to figure out start index
if(data[lastIndex - 1] != del)
{
for(int i = lastIndex; i > 0; i--)
{
if(data[i] == del)
{
startIndex = i;
}
}
//add the result in another list
for(int i = 0; i <= lastIndex - startIndex; i++)
{
tos.Add(data[i]);
}
string shit = string.Empty;
foreach (var b in tos)
shit += (int)b + ", ";
//print result in a textbox
AddTextToLogTextView(shit + "\r\n");
}
Solutions
I've prepared three possible solution of taking the last packet from input buffor:
Using LINQ
public static byte[] GetLastPacketUsingLINQ(byte[] input, byte delimiter)
{
var part = input.Reverse()
.SkipWhile(i => i != delimiter)
.SkipWhile(i => i == delimiter)
.TakeWhile(i => i != delimiter)
.Reverse();
return (new byte[] { delimiter }).Concat(part).Concat(new byte[] { delimiter }).ToArray();
}
Using string.Split
public static byte[] GetLastPacketUsingString(byte[] input, byte delimiter)
{
var encoding = System.Text.Encoding.GetEncoding("iso-8859-1");
string inputString = encoding.GetString(input);
var parts = inputString.Split(new[] { (char)delimiter }, StringSplitOptions.RemoveEmptyEntries);
return encoding.GetBytes((char)delimiter + parts[parts.Length - 2] + (char)delimiter);
}
Using while loop and indexers
public static byte[] GetLastPacketUsingIndexers(byte[] input, byte delimiter)
{
int end = input.Length - 1;
while (input[end--] != delimiter) ;
int start = end - 1;
while (input[start--] != delimiter) ;
var result = new byte[end - start];
Array.Copy(input, start + 1, result, 0, result.Length);
return result;
}
Performance
I've also performed some very simple performance tests. Here are the results:
LINQ version result:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
String version result:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
Indexers version result:
126 69 0 0 1 0 2 2 34 6 0 5 232 125 93 126
LINQ version time: 64ms (106111 ticks)
String version time: 2ms (3422 ticks)
Indexers version time: 1ms (2359 ticks)
Conclusion
As you can see, the simplest one is also the best one here.
You may think that LINQ is an answer for every problem, but some time it's really better idea to write simpler solution manually instead of using LINQ methods.
Using LINQ this can be done in a single line of code if the following two rules can be applied to the buffer:
The buffer contains at least one complete package surrounded by the
given delimiter.
Each packet contains at least one byte of data.
Here is the code:
var data = (byte[])msg.Obj;
byte delimiter = 126;
var packet = data.Reverse()
.SkipWhile(b => b != delimiter)
.SkipWhile(b => b == delimiter)
.TakeWhile(b => b != delimiter)
.Reverse();
(Ok, this was more than a single line because I splitted it into multiple lines for better readability.)
EDIT: Removed the call to Take(1) because that would always return an empty sequence. The result however doesn't contain delimiter this way.
And here is how it works:
Since we want to find the last packet we can reverse the data:
var reversed = data.Reverse();
The buffer can end with a packet which is not complete yet. So let's skip that:
reversed = reversed.SkipWhile(b => b != delimiter);
reversed is now either empty or it starts with delimiter. Since we assumed that the buffer always contains at least one complete packet we can already take the next byte for our result because we know it is the delimiter:
var packet = reversed.Take(1);
In the sequence we can now skip one byte. If the delimiter we found was actually the start of a new packet the remaining sequence will start with another delimiter so we have to skip that also:
reversed = reversed.Skip(1);
if (reversed.First() == delimiter)
{
reversed.Skip(1);
}
Since we know that a packet can not be empty because it contains a 3 bytes CRC we could have written:
reversed = reversed.SkipWhile(b => b == delimiter);
Now the actual data follows:
packet = packet.Concat(reversed.TakeWhile(b => b != delimiter));
reversed = reversed.SkipWhile(b => b != delimiter);
The next byte is the delimiter which marks the start of the packet:
packet = packet.Concat(reversed.Take(1));
The last thing to do is to reverse the result again:
packet = packet.Reverse();
Maybe you want to put this into a method:
public IEnumerable<byte> GetPacket(byte[] data, byte delimiter)
{
yield return delimiter;
foreach (byte value in data.Reverse()
.SkipWhile(b => b != delimiter)
.SkipWhile(b => b == delimiter)
.TakeWhile(b => b != delimiter))
{
yield return value;
}
yield return delimiter;
}
You will have to call Reverse on the return value of this method.
If performance matters you can use the same algorithm on the underlying array. This way it will be about 20 times faster:
int end = data.Length - 1;
while (data[end] != delimiter)
end--;
while (data[end] == delimiter)
end--;
int start = end;
while (data[start] != delimiter)
start--;
byte[] result = new byte[end - start + 2]; // +2 to include delimiters
Array.Copy(data, start, result, 0, result.Length);
There actually are various ways to solve your question, the simplest idea is detect double 126(0x7e), and doesn't matter other things such like CRC.
The basic implemention of this concept would be like this
Code as simple
var list=new List<byte[]>();
int i=0, j=0;
for(; i<data.Length; ++i)
if(i>0&&0x7e==data[i]&&0x7e==data[i-1]) {
list.Add(data.Skip(j).Take(i-j).ToArray());
j=i;
}
list.Add(data.Skip(j).Take(i-j).ToArray());
Base on my old answer of Konami Code in C#, and it even used to solve this question: Double characters shown when typing special characters while logging keystrokes in c#.
Code with a sequence detector
public partial class TestClass {
public static void TestMethod() {
var data=(
new[] {
126, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0, 1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93, 126,
126, 69, 0, 0
}).Select(x => (byte)x).ToArray();
var list=new List<List<byte>>();
foreach(var x in data) {
if(list.Count<1||SequenceCapturer.Captured((int)x))
list.Add(new List<byte>());
list.Last().Add(x);
}
foreach(var byteList in list)
Debug.Print("{0}", byteList.Select(x => x.ToString("x2")).Aggregate((a, b) => a+"\x20"+b));
}
}
public class SequenceCapturer {
public int Count {
private set;
get;
}
public int[] Sequence {
set;
get;
}
public bool Captures(int value) {
for(var i=Sequence.Length; i-->0; ) {
if(Sequence[i]!=value) {
if(0==i)
Count=0;
continue;
}
if(Count!=i)
continue;
++Count;
break;
}
var x=Sequence.Length==Count;
Count=x?0:Count;
return x;
}
public SequenceCapturer(int[] newSequence) {
Sequence=newSequence;
}
public SequenceCapturer()
: this(new[] { 0x7e, 0x7e }) {
}
public static bool Captured(int value) {
return Instance.Captures(value);
}
public static SequenceCapturer Instance=new SequenceCapturer();
}
Or if you would like to write it full in Linq, you might want to try the following. You even don't need to use List, packetArray gives you an array of byte arrays directly.
The lets are intended to break the code into lines, otherwise it would be an extreme long statement in one line. If you consider one line is the best, then I will.
Code of packetArray
var packetArray=(
from sig in new[] { new byte[] { 0x7e, 0x7e } }
let find=new Func<byte[], int, IEnumerable<byte>>((x, i) => x.Skip(i).Take(sig.Length))
let isMatch=new Func<IEnumerable<byte>, bool>(sig.SequenceEqual)
let filtered=data.Select((x, i) => 0==i||isMatch(find(data, i-1))?i:~0)
let indices=filtered.Where(i => ~0!=i).Concat(new[] { data.Length }).ToArray()
from index in Enumerable.Range(1, indices.Length-1)
let skipped=indices[index-1]
select data.Skip(skipped).Take(indices[index]-skipped).ToArray()).ToArray();
Code for output
foreach(var byteArray in packetArray)
Debug.Print("{0}", byteArray.Select(x => x.ToString("x2")).Aggregate((a, b) => a+"\x20"+b));
However, even in the same concept of solution, there would be various ways as I mentioned before. I'd strongly recommend that don't involve additional conditions like something about CRC, which might make things more complicated.
Since you're looking for the last packet, it's much easier to reverse the byte[] and look for the first packet. Your two packet delimiters are not just 126. They are 126, 69 for the start and 126, 126 for the end unless the end of the packet is the last byte recieved, which makes the end delimiter 126.
I would suggest using a method simular to this:
public static byte[] GetMessage(byte[] msg)
{
//Set delimiters
byte delimit = 126;
byte startDelimit = 69;
//Reverse the msg so we can find the last packet
List<byte> buf = msg.Reverse().ToList();
//set indices to impossible values to check for failures
int startIndex = -1;
int endIndex = -1;
//loop through the message
for (int i = 0; i < buf.Count - 1; i++)
{
//find either a double 126, or 126 as the last byte (message just ended)
if (buf[i] == delimit && (buf[i + 1] == delimit || i == 0))
{
if (i == 0)
{
startIndex = i;
i++;
}
else
{
startIndex = i + 1;
i += 2;
}
continue;
}
//Only process if we've found the start index
if (startIndex != -1)
{
//check if the byte is 69 followed by 126
if (buf[i] == startDelimit && buf[i + 1] == delimit)
{
endIndex = i + 1;
break;
}
}
}
//make sure we've found a message
if (!(startIndex == -1 || endIndex==-1))
{
//get the message and reverse it to be the original packet
byte[] revRet = new byte[endIndex - startIndex];
Array.Copy(buf.ToArray(), startIndex, revRet, 0, endIndex - startIndex);
return revRet.Reverse().ToArray();
}
return new byte[1];
}
I'm not totally sure if the indices of the copy are completely correct, but this should be the jist of it.
since you may receive incomplete data, you must store the last incomplete buffer.
this is sample case, First Receive :
126, 6, 0, 5, 232, 125, 93, 126, 126, 69, 0,
0, 1, 0, 2, 2, 34, 6 , 0 , 5 , 232, 125,
93, 126, 126, 69, 0, 0, 1 , 0, 2, 2, 34,
6, 0, 5, 232, 125, 93, 126, 126, 69, 0, 0 ,
1, 0, 2, 2, 34, 6, 0, 5, 232, 125, 93,
126, 126, 69, 0, 0
the second stream :
69, 0, 0 , 1, 0, 2, 2, 34, 6, 0, 126
and the code:
List<byte> lastBuf = new List<byte>();
List<byte[]> Extract(byte[] data, byte delim)
{
List<byte[]> result = new List<byte[]>();
for (int i = 0; i < data.Length; i++)
{
if (lastBuf.Count > 0)
{
if(data[i] == delim)
{
result.Add(lastBuf.ToArray());
lastBuf.Clear();
}
else
{
lastBuf.Add(data[i]);
}
}
else
{
if(data[i] != 126)
{
lastBuf.Add(data[i]);
}
}
}
return result;
}
result :
Related
Removing leading 0s in a byte array
I have a byte array as follows - byte[] arrByt = new byte[] { 0xF, 0xF, 0x11, 0x4 }; so in binary arrByt = 00001111 00001111 00010001 000000100 Now I want to create a new byte array by removing leading 0s for each byte from arrByt arrNewByt = 11111111 10001100 = { 0xFF, 0x8C }; I know that this can be done by converting the byte values into binary string values, removing the leading 0s, appending the values and converting back to byte values into the new array. However this is a slow process for a large array. Is there a faster way to achieve this (like logical operations, bit operations, or other efficient ways)? Thanks.
This should do the job quite fast. At least only standard loops and operators. Give it a try, will also work for longer source arrays. // source array of bytes var arrByt = new byte[] {0xF, 0xF, 0x11, 0x4 }; // target array - first with the size of the source array var targetArray = new byte[arrByt.Length]; // bit index in target array // from left = byte 0, bit 7 = index 31; to the right = byte 4, bit 0 = index 0 var targetIdx = targetArray.Length * 8 - 1; // go through all bytes of the source array from left to right for (var i = 0; i < arrByt.Length; i++) { var startFound = false; // go through all bits of the current byte from the highest to the lowest for (var x = 7; x >= 0; x--) { // copy the bit if it is 1 or if there was already a 1 before in this byte if (startFound || ((arrByt[i] >> x) & 1) == 1) { startFound = true; // copy the bit from its position in the source array to its new position in the target array targetArray[targetArray.Length - ((targetIdx / 8) + 1)] |= (byte) (((arrByt[i] >> x) & 1) << (targetIdx % 8)); // advance the bit + byte position in the target array one to the right targetIdx--; } } } // resize the target array to only the bytes that were used above Array.Resize(ref targetArray, (int)Math.Ceiling((targetArray.Length * 8 - (targetIdx + 1)) / 8d)); // write target array content to console for (var i = 0; i < targetArray.Length; i++) { Console.Write($"{targetArray[i]:X} "); } // OUTPUT: FF 8C
If you are trying to find the location of the most-significant bit, you can do a log2() of the byte (and if you don't have log2, you can use log(x)/log(2) which is the same as log2(x)) For instance, the number 7, 6, 5, and 4 all have a '1' in the 3rd bit position (0111, 0110, 0101, 0100). The log2() of them are all between 2 and 2.8. Same thing happens for anything in the 4th bit, it will be a number between 3 and 3.9. So you can find out the Most Significant Bit by adding 1 to the log2() of the number (round down). floor(log2(00001111)) + 1 == floor(3.9) + 1 == 3 + 1 == 4 You know how many bits are in a byte, so you can easily know the number of bits to shift left: int numToShift = 8 - floor(log2(bytearray[0])) + 1; shiftedValue = bytearray[0] << numToShift; From there, it's just a matter of keeping track of how many outstanding bits (not pushed into a bytearray yet) you have, and then pushing some/all of them on. The above code would only work for the first byte array. If you put this in a loop, the numToShift would maybe need to keep track of the latest empty slot to shift things into (you might have to shift right to fit in current byte array, and then use the leftovers to put into the start of the next byte array). So instead of doing "8 -" in the above code, you would maybe put the starting location. For instance, if only 3 bits were left to fill in the current byte array, you would do: int numToShift = 3 - floor(log2(bytearray[0])) + 1; So that number should be a variable: int numToShift = bitsAvailableInCurrentByte - floor(log2(bytearray[0])) + 1;
Please check this code snippet. This might help you. byte[] arrByt = new byte[] { 0xF, 0xF, 0x11, 0x4 }; byte[] result = new byte[arrByt.Length / 2]; var en = arrByt.GetEnumerator(); int count = 0; byte result1 = 0; int index = 0; while (en.MoveNext()) { count++; byte item = (byte)en.Current; if (count == 1) { while (item < 128) { item = (byte)(item << 1); } result1 ^= item; } if (count == 2) { count = 0; result1 ^= item; result[index] = result1; index++; result1 = 0; } } foreach (var s in result) { Console.WriteLine(s.ToString("X")); }
Convert byte[] to UInt16.
I have a 2d array of UInt16s which I've converted to raw bytes - I would like to take those bytes and convert them back into the original 2D array. I've managed to do this with a 2d array of doubles, but I can't figure out how to do it with UInt16. Here's my code: UInt16[,] dataArray; //This array is populated with this data: [4 6 2] [0 2 0] [1 3 4] long byteCountUInt16Array = dataArray.GetLength(0) * dataArray.GetLength(1) * sizeof(UInt16); var bufferUInt16 = new byte[byteCountUInt16Array]; Buffer.BlockCopy(newUint16Array, 0, bufferUInt16, 0, bufferUInt16.Length); //Here is where I try to convert the values and print them out to see if the values are still the same: UInt16[] originalUInt16Values = new UInt16[bufferUInt16.Length / 8]; for (int i = 0; i < 5; i++) { originalUInt16Values[i] = BitConverter.ToUInt16(bufferUInt16, i * 8); Console.WriteLine("Values: " + originalUInt16Values[i]); } The print statement does not show the same values as the original 2d array. I'm pretty new to coding with bytes and UInt16 so most of this I'm learning in the process. *Also, I know the last chunk of my code isn't putting values into a 2d array like the original array - right now I'm just trying to print out the values to see if they even match the original data.
If what you want is just to cast UInt16[,]->Byte, and then Byte->UInt16 you can do another Block copy, which is very fast at run-time, code should look like this: UInt16[,] dataArray = new UInt16[,] { {4, 6, 2}, {0, 2, 0}, {1, 3, 4} }; for (int j = 0; j < 3; j++) { for (int i = 0; i < 3; i++) { Console.WriteLine("Value[" + i + ", " + j + "] = " + dataArray[j,i]); } } long byteCountUInt16Array = dataArray.GetLength(0) * dataArray.GetLength(1) * sizeof(UInt16); var bufferUInt16 = new byte[byteCountUInt16Array]; Buffer.BlockCopy(dataArray, 0, bufferUInt16, 0, bufferUInt16.Length); //Here is where I try to convert the values and print them out to see if the values are still the same: UInt16[] originalUInt16Values = new UInt16[bufferUInt16.Length / 2]; Buffer.BlockCopy(bufferUInt16, 0, originalUInt16Values, 0, BufferUInt16.Length); for (int i = 0; i < 5; i++) { //originalUInt16Values[i] = BitConverter.ToUInt16(bufferUInt16, i * 8); Console.WriteLine("Values---: " + originalUInt16Values[i]); } by the way, you only divided each UInt16 into two bytes, so you should calculate your new size dividing by two, not eight
The program public static void Main(string[] args) { UInt16[,] dataArray = new ushort[,]{ {4,6,2}, {0,2,0}, {1,3,4}}; //This array is populated with this data: long byteCountUInt16Array = dataArray.GetLength(0) * dataArray.GetLength(1) * sizeof(UInt16); var byteBuffer = new byte[byteCountUInt16Array]; Buffer.BlockCopy(dataArray, 0, byteBuffer, 0, byteBuffer.Length); for(int i=0; i < byteBuffer.Length; i++) { Console.WriteLine("byteBuf[{0}]= {1}", i, byteBuffer[i]); } Console.WriteLine("Byte buffer len: {0} data array len: {1}", byteBuffer.Length, dataArray.GetLength(0)* dataArray.GetLength(1)); UInt16[] originalUInt16Values = new UInt16[byteBuffer.Length / 2]; for (int i = 0; i < byteBuffer.Length; i+=2) { ushort _a = (ushort)( (byteBuffer[i]) | (byteBuffer[i+1]) << 8); originalUInt16Values[i/2] = _a; Console.WriteLine("Values: " + originalUInt16Values[i/2]); } } Outputs byteBuf[0]= 4 byteBuf[1]= 0 byteBuf[2]= 6 byteBuf[3]= 0 byteBuf[4]= 2 byteBuf[5]= 0 byteBuf[6]= 0 byteBuf[7]= 0 byteBuf[8]= 2 byteBuf[9]= 0 byteBuf[10]= 0 byteBuf[11]= 0 byteBuf[12]= 1 byteBuf[13]= 0 byteBuf[14]= 3 byteBuf[15]= 0 byteBuf[16]= 4 byteBuf[17]= 0 Byte buffer len: 18 data array len: 9 Values: 4 Values: 6 Values: 2 Values: 0 Values: 2 Values: 0 Values: 1 Values: 3 Values: 4 You see that a ushort, aka UInt16 is stored in a byte-order in which 4 = 0x04 0x00, which is why I chose the conversion formula ushort _a = (ushort)( (byteBuffer[i]) | (byteBuffer[i+1]) << 8); Which will grab the byte at index i and take the next byte at i+1 and left shift it by the size of a byte (8 bits) to make up the 16 bits of a ushort. In orhter words, ushort _a = 0x[second byte] 0x[first byte], which is then repeated. This conversion code is specific for the endianess of the machine you are on and thus non-portable. Also I fixed the error where the byteBuffer array was to big because it was multiplied with factor 8. A ushort is double the size of a byte, thus we only need factor 2 in the array length.
Addressing the title of your question (Convert byte[] to UInt16): UInt16 result = (UInt16)BitConverter.ToInt16(yourByteArray, startIndex = 0);
Your casting up so you should be able to do things implicitly var list = new List<byte> { 1, 2 , var uintList = new List<UInt16>(); //Cast in your select uintList = list.Select(x => (UInt16)x).ToList();
Replace Hex String in File
After banging my head on it for hours, I am at my wits end. I have a hex string in a file that is "68 39 30 00 00". The "39 30" is the decimal value "12345" which I am wanting to replace, and the "68" and "00 00" are just to ensure there is a single match. I want to pass in a new decimal value such as "12346", and replace the existing value in the file. I have tried converting everything back and fourth between hex, byte arrays, and so on and feel it has to be much simpler than I am making it out to be. static void Main(string[] args) { // Original Byte string to find and Replace "12345" byte[] original = new byte[] { 68, 39, 30, 00, 00 }; int newPort = Convert.ToInt32("12346"); string hexValue = newPort.ToString("X2"); byte[] byteValue = StringToByteArray(hexValue); // Build Byte Array of the port to replace with. Starts with /x68 and ends with /x00/x00 byte[] preByte = new byte[] { byte.Parse("68", System.Globalization.NumberStyles.HexNumber) }; byte[] portByte = byteValue; byte[] endByte = new byte[] { byte.Parse("00", System.Globalization.NumberStyles.HexNumber), byte.Parse("00", System.Globalization.NumberStyles.HexNumber) }; byte[] replace = new byte[preByte.Length + portByte.Length + endByte.Length]; preByte.CopyTo(replace, 0); portByte.CopyTo(replace, preByte.Length); endByte.CopyTo(replace, (preByte.Length + portByte.Length)); Patch("Server.exe", "Server1.exe", original, replace); } static private byte[] StringToByteArray(string hex) { return Enumerable.Range(0, hex.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .ToArray(); }
The decimal value of 39 30 is 14640 so it will check for the 14640 which is not present. Hex value for 12345 is 30 39. Just correct the values and your program will work fine.
Combine one Byte[] to Single Array
i do have an byte array included a range of numbers... t Block and not the rest! How can i have all block 4-8 in Temp[] ??
Elements 4-8 (or in reality index 3-7) is 5 bytes. Not 4. You have the source offset and count mixed up: Buffer.BlockCopy(bResponse, 3, temp, 0, 5); Now temp will contain [23232]. If you want the last 4 bytes then use this: Buffer.BlockCopy(bResponse, 4, temp, 0, 4); Now temp will contain [3232]. To convert this to an int: if (BitConverter.IsLittleEndian) Array.Reverse(temp); int i = BitConverter.ToInt32(temp, 0); Edit: (After your comment that [43323232] actually is {43, 32, 32, 32}) var firstByte = temp[0]; // This is 43 var secondByte = temp[1]; // This is 32 var thirdByte = temp[2]; // 32 var fourthByte = temp[3]; // 32 If you want to convert this to an int then the BitConverter example above still works.
Convert int to a bit array in .NET
How can I convert an int to a bit array? If I e.g. have an int with the value 3 I want an array, that has the length 8 and that looks like this: 0 0 0 0 0 0 1 1 Each of these numbers are in a separate slot in the array that have the size 8.
Use the BitArray class. int value = 3; BitArray b = new BitArray(new int[] { value }); If you want to get an array for the bits, you can use the BitArray.CopyTo method with a bool[] array. bool[] bits = new bool[b.Count]; b.CopyTo(bits, 0); Note that the bits will be stored from least significant to most significant, so you may wish to use Array.Reverse. And finally, if you want get 0s and 1s for each bit instead of booleans (I'm using a byte to store each bit; less wasteful than an int): byte[] bitValues = bits.Select(bit => (byte)(bit ? 1 : 0)).ToArray();
To convert the int 'x' int x = 3; One way, by manipulation on the int : string s = Convert.ToString(x, 2); //Convert to binary in a string int[] bits= s.PadLeft(8, '0') // Add 0's from left .Select(c => int.Parse(c.ToString())) // convert each char to int .ToArray(); // Convert IEnumerable from select to Array Alternatively, by using the BitArray class- BitArray b = new BitArray(new byte[] { x }); int[] bits = b.Cast<bool>().Select(bit => bit ? 1 : 0).ToArray();
Use Convert.ToString (value, 2) so in your case string binValue = Convert.ToString (3, 2);
I would achieve it in a one-liner as shown below: using System; using System.Collections; namespace stackoverflowQuestions { class Program { static void Main(string[] args) { //get bit Array for number 20 var myBitArray = new BitArray(BitConverter.GetBytes(20)); } } } Please note that every element of a BitArray is stored as bool as shown in below snapshot: So below code works: if (myBitArray[0] == false) { //this code block will execute } but below code doesn't compile at all: if (myBitArray[0] == 0) { //some code }
I just ran into an instance where... int val = 2097152; var arr = Convert.ToString(val, 2).ToArray(); var myVal = arr[21]; ...did not produce the results I was looking for. In 'myVal' above, the value stored in the array in position 21 was '0'. It should have been a '1'. I'm not sure why I received an inaccurate value for this and it baffled me until I found another way in C# to convert an INT to a bit array: int val = 2097152; var arr = new BitArray(BitConverter.GetBytes(val)); var myVal = arr[21]; This produced the result 'true' as a boolean value for 'myVal'. I realize this may not be the most efficient way to obtain this value, but it was very straight forward, simple, and readable.
To convert your integer input to an array of bool of any size, just use LINQ. bool[] ToBits(int input, int numberOfBits) { return Enumerable.Range(0, numberOfBits) .Select(bitIndex => 1 << bitIndex) .Select(bitMask => (input & bitMask) == bitMask) .ToArray(); } So to convert an integer to a bool array of up to 32 bits, simply use it like so: bool[] bits = ToBits(65, 8); // true, false, false, false, false, false, true, false You may wish to reverse the array depending on your needs. Array.Reverse(bits);
int value = 3; var array = Convert.ToString(value, 2).PadLeft(8, '0').ToArray();
public static bool[] Convert(int[] input, int length) { var ret = new bool[length]; var siz = sizeof(int) * 8; var pow = 0; var cur = 0; for (var a = 0; a < input.Length && cur < length; ++a) { var inp = input[a]; pow = 1; if (inp > 0) { for (var i = 0; i < siz && cur < length; ++i) { ret[cur++] = (inp & pow) == pow; pow *= 2; } } else { for (var i = 0; i < siz && cur < length; ++i) { ret[cur++] = (inp & pow) != pow; pow *= 2; } } } return ret; }
I recently discovered the C# Vector<T> class, which uses hardware acceleration (i.e. SIMD: Single-Instruction Multiple Data) to perform operations across the vector components as single instructions. In other words, it parallelizes array operations, to an extent. Since you are trying to expand an integer bitmask to an array, perhaps you are trying to do something similar. If you're at the point of unrolling your code, this would be an optimization to strongly consider. But also weigh this against the costs if you are only sparsely using them. And also consider the memory overhead, since Vectors really want to operate on contiguous memory (in CLR known as a Span<T>), so the kernel may be having to twiddle bits under the hood when you instantiate your own vectors from arrays. Here is an example of how to do masking: //given two vectors Vector<int> data1 = new Vector<int>(new int[] { 1, 0, 1, 0, 1, 0, 1, 0 }); Vector<int> data2 = new Vector<int>(new int[] { 0, 1, 1, 0, 1, 0, 0, 1 }); //get the pairwise-matching elements Vector<int> mask = Vector.Equals(data1, data2); //and return values from another new vector for matches Vector<int> whenMatched = new Vector<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8 }); //and zero otherwise Vector<int> whenUnmatched = Vector<int>.Zero; //perform the filtering Vector<int> result = Vector.ConditionalSelect(mask, whenMatched, whenUnmatched); //note that only the first half of vector components render in the Debugger (this is a known bug) string resultStr = string.Join("", result); //resultStr is <0, 0, 3, 4, 5, 6, 0, 0> Note that the VS Debugger is bugged, showing only the first half of the components of a vector. So with an integer as your mask, you might try: int maskInt = 0x0F;//00001111 in binary //convert int mask to a vector (anybody know a better way??) Vector<int> maskVector = new Vector<int>(Enumerable.Range(0, Vector<int>.Count).Select(i => (maskInt & 1<<i) > 0 ? -1 : 0).ToArray()); Note that the (signed integer) -1 is used to signal true, which has binary representation of all ones. Positive 1 does not work, and you can cast (int)-1 to uint to get every bit of the binary enabled, if needed (but not by using Enumerable.Cast<>()). However this only works for int32 masks up to 2^8 because of the 8-element capacity in my system (that supports 4x64-bit chunks). This depends on the execution environment based on the hardware capabilities, so always use Vector<T>.Capacity. You therefore can get double the capacity with shorts as ints as longs (the new Half type isn't yet supported, nor is "Decimal", which are the corresponding float/double types to int16 and int128): ushort maskInt = 0b1111010101010101; Vector<ushort> maskVector = new Vector<ushort>(Enumerable.Range(0, Vector<ushort>.Count).Select(i => (maskInt & 1<<i) > 0 ? -1 : 0).Select(x => (ushort)x).ToArray()); //string maskString = string.Join("", maskVector);//<65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 0, 65535, 65535, 65535, 65535> Vector<ushort> whenMatched = new Vector<ushort>(Enumerable.Range(1, Vector<ushort>.Count).Select(i => (ushort)i).ToArray());//{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} Vector<ushort> whenUnmatched = Vector<ushort>.Zero; Vector<ushort> result = Vector.ConditionalSelect(maskVector, whenMatched, whenUnmatched); string resultStr = string.Join("", result);//<1, 0, 3, 0, 5, 0, 7, 0, 9, 0, 11, 0, 13, 14, 15, 16> Due to how integers work, being signed or unsigned (using the most significant bit to indicate +/- values), you might need to consider that too, for values like converting 0b1111111111111111 to a short. The compiler will usually stop you if you try to do something that appears to be stupid, at least. short maskInt = unchecked((short)(0b1111111111111111)); Just make sure you don't confuse the most-significant bit of an int32 as being 2^31.
Using & (AND) Bitwise Operators The answers above are all correct and effective. If you wanted to do it old-school, without using BitArray or String.Convert(), you would use bitwise operators. The bitwise AND operator & takes two operands and returns a value where every bit is either 1, if both operands have a 1 in that place, or a 0 in every other case. It's just like the logical AND (&&), but it takes integral operands instead of boolean operands. Ex. 0101 & 1001 = 0001 Using this principle, any integer AND the maximum value of that integer is itself. byte b = 0b_0100_1011; // In base 10, 75. Console.WriteLine(b & byte.MaxValue); // byte.MaxValue = 255 Result: 75 Bitwise AND in a loop We can use this to our advantage to only take specific bits from an integer by using a loop that goes through every bit in a positive 32-bit integer (i.e., uint) and puts the result of the AND operation into an array of strings which will all be "1" or "0". A number that has a 1 at only one specific digit n is equal to 2 to the nth power (I typically use the Math.Pow() method). public static string[] GetBits(uint x) { string[] bits = new string[32]; for (int i = 0; i < 32; i++) { uint bit = x & Math.Pow(2, i); if (bit == 1) bits[i] = "1"; else bits[i] = "0"; } return bits; } If you were to input, say, 1000 (which is equivalent to binary 1111101000), you would get an array of 32 strings that would spell 0000 0000 0000 0000 0000 0011 1110 1000 (the spaces are just for readability).