views:

117

answers:

5

Let's say we have a binary file that contains 2 bytes that form an integer, in reverse.

So for example, the bytes show like this: (hexadecimal)

EB 03 00 00

Which should be interpreted as this:

00 00 03 EB

Which C# should be able to input as decimal 1003. Is this possible if you have the EB and 03 bytes already in memory in 2 different variables? Is there some math I can apply here to form decimal 1003 from the numbers 235 and 3? Or should I do it completely different?

Thanks in advance!

+4  A: 

You should be able to just read the value from the binary file into an unsigned 32 bit integer:

UInt32 myValue;

using this method from your BinaryReader object:

myValue = reader.ReadUInt32();

On a Little Endian system (i.e. Intel), the byte order will reverse automatically.

To get your array of bytes:

byte[] b = BitConverter.GetBytes(myValue);
Robert Harvey
Thanks for the quick answer! But how can I do this if I have an array of bytes? Like bData[0] is EB, bData[1] = 03, bData[2] = 00 and bData[3] = 00. Would setting an unsigned short variable as bData[0]+bData[1]...[3] work?
Angelo Geels
@Angelo: You can use the BitConverter class in the Framework to convert between byte arrays and UInt16 values.
Robert Harvey
@Angelo: Why would adding the bytes together work? Just do what 0xA3 said: use `BinaryReader.ReadUint32`.
Steven Sudit
+8  A: 

What you talk about is called Endianness, in particular Little Endian format.

In C#, it's probably easiest to use a BinaryReader or BinaryWriter to read from binary files which wrap the correct conversion of the byte order.

The following sample shows how to use a BinaryReader to get the correct integer interpretation of a number in Little Endian format:

using System.IO;

class EndiannessSample
{
    static void Main(string[] args)
    {
        using (MemoryStream ms = new MemoryStream())
        {
            // write bytes in little-endian format
            ms.WriteByte(0xEB);
            ms.WriteByte(0x03);
            ms.WriteByte(0x00);
            ms.WriteByte(0x00);
            ms.Position = 0;

            using (BinaryReader reader = new BinaryReader(ms))
            {
                int i = reader.ReadInt32(); // decimal value of i is 1003
            }
        }
    }
}

Little endian is the standard on Intel (and Windows) platforms. In case you have to deal with data in Big Endian format (e.g. when importing files created on an old Macintosh) there is no direct support within .NET. You can write a simple utility function for converting endianness using the BitConverter class. In the sample above you could do the following to cope with Big Endian (on a Little Endian platform):

using (MemoryStream ms = new MemoryStream())
{
    // write bytes in big-endian format
    ms.WriteByte(0x00);
    ms.WriteByte(0x00);
    ms.WriteByte(0x03);
    ms.WriteByte(0xEB);

    ms.Position = 0;
    using (BinaryReader reader = new BinaryReader(ms))
    {
        byte[] temp = reader.ReadBytes(4);
        if (BitConverter.IsLittleEndian)
        {
            // reverse the byte order only if we are on a little-endian system,
            // because the BitConverter is aware of the endianness of the system
            //
            Array.Reverse(temp);
        }
        int i = BitConverter.ToInt32(temp, 0);
    }
}

LukeH provided a link that further discusses problems related to Endianness, e.g. when targeting Xbox 360 (which happens to be a Big Endian platform):

One Little, Two Little, Three Little Endian... 0x0A Big Endian Bugs

Update

The MiscUtil library provides a binary reader/writer class that can be configured for a specific Endianness:

MiscUtil.IO.EndianBinary{Writer/Reader}
0xA3
This is a pretty good answer too. I like these methods! Thanks!
Angelo Geels
Well, I'm upvoting this one, because it's correct and the most complete answer.
Robert Harvey
+1 Good example
SwDevMan81
Ditto. It should never have been downvoted, even when it was just correct and short.
Steven Sudit
@0xA3: It is worth noting that there is a static readonly boolean member in the BitConverter class called `IsLittleEndian`, which is set to `true`. Theoretically, if the framework is ever ported to a Posix system, they can set this member to `false`, and everything should work just fine.
Robert Harvey
Great Answer, I like the examples
MikeAinOz
@Robert: It's not just a theoretical possibility: the XBox 360 is big-endian and runs a version of the framework; it's also possible -- I'm not sure -- that the compact framework might run on some big-endian platforms; and then, of course, there's Mono too. If you need fixed endianness regardless of where your code runs then you *can't* use `BitConverter`, since it always uses the current system endianness. http://blogs.msdn.com/b/robunoki/archive/2006/04/05/568737.aspx
LukeH
@LukeH: That's a great article, I updated my answer so that your link does not got lost in the comments.
0xA3
+1  A: 

This is called Little Endian and it’s the normal byte-order in Windows.

You can use BinaryReader to read integers from a stream in this form.

If you want to roll your own...

var b1 = file.ReadByte();
var b2 = file.ReadByte();
var b3 = file.ReadByte();
var b4 = file.ReadByte();
var result = b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);

(Of course, in real code, check for premature end of file, etc.)

If you already have the data in a byte array:

byte[] b = ...;
var result = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
Timwi
I can see you downvoting, but I think you got it backwards. I've done this before on an Intel system.
Robert Harvey
This method is returning negative values for me. Any idea why?
Angelo Geels
@Angelo: because it's wrong. It's not Big-Endian.
Robert Harvey
LukeH's method worked for me, but that's just for 2 bytes. I assume Timwi above is doing it the wrong way around. Am I right?
Angelo Geels
Perfect. Your edit did the trick. Thanks!
Angelo Geels
@Robert: I compiled my code and it produces the output `1003` as expected. @0xA3: Please tell me what is wrong, then I’ll fix it.
Timwi
Hmm. Confused here. It returns the correct integer value for all files I feed it.
Angelo Geels
@Timwi: What's wrong is that your code is little-endian, just like the data, but you keep calling it big-endian.
Steven Sudit
The code is fine, but unnecessary. If you read an `Int32` or `UInt32` on an Intel machine, using a `BinaryReader`, your byte order will come out just fine. And it's not Big Endian.
Robert Harvey
Go read http://en.wikipedia.org/wiki/Endianness until you understand the difference.
Steven Sudit
@Robert, @0xA3, @Steven: You were right, I got the terminology wrong, and `BinaryReader` does do it. I have corrected the answer accordingly.
Timwi
Thanks for the update, removed my downvote already.
0xA3
+2  A: 

Is this possible if you have the EB and 03 bytes already in memory in 2 different variables?

Yes...

byte x = 0xEB;
byte y = 0x03;

int i = (y << 8) | x;
// or
short s = (short)((y << 8) | x);

Or if you have four bytes that you want to convert to an int:

byte a = 0x01;
byte b = 0x02;  
byte c = 0x03;
byte d = 0x04;

int i = (a << 24) | (b << 16) | (c << 8) | d;
// or if you want to reverse the endianness...
int j = (d << 24) | (c << 16) | (b << 8) | a;
LukeH
This works great. But what about 4 bytes?
Angelo Geels
@Angelo: You didn't ask about 4 bytes, but I'll edit to cover that too.
LukeH
Luke, Timwi's edit to his answer worked for me. Don't worry ;)
Angelo Geels
+2  A: 

You could use the BitConverter class has Robert mentioned. A simple example:

     byte[] byte_arr = new byte[] { 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89 };
     UInt16 uint1 = BitConverter.ToUInt16(byte_arr, 0); // 0x2312
     UInt16 uint2 = BitConverter.ToUInt16(byte_arr, 4); // 0x6756
SwDevMan81