4

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!

Codecat
  • 2,213
  • 3
  • 28
  • 39

5 Answers5

10

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}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
  • This is a pretty good answer too. I like these methods! Thanks! – Codecat Sep 23 '10 at 22:26
  • Well, I'm upvoting this one, because it's correct and the most complete answer. – Robert Harvey Sep 23 '10 at 22:28
  • Ditto. It should never have been downvoted, even when it was just correct and short. – Steven Sudit Sep 23 '10 at 22:33
  • 1
    @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 Sep 23 '10 at 22:56
  • Great Answer, I like the examples – MikeAinOz Sep 23 '10 at 23:36
  • 2
    @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 Sep 24 '10 at 09:05
  • @LukeH: That's a great article, I updated my answer so that your link does not got lost in the comments. – Dirk Vollmar Sep 24 '10 at 09:21
4

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
  • 178,213
  • 47
  • 333
  • 501
  • 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? – Codecat Sep 23 '10 at 22:17
  • 1
    @Angelo: You can use the BitConverter class in the Framework to convert between byte arrays and UInt16 values. – Robert Harvey Sep 23 '10 at 22:22
  • @Angelo: Why would adding the bytes together work? Just do what 0xA3 said: use `BinaryReader.ReadUint32`. – Steven Sudit Sep 23 '10 at 22:26
2

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
  • 263,068
  • 57
  • 365
  • 409
2

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
  • 48,814
  • 22
  • 151
  • 184
1

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
  • 65,159
  • 33
  • 165
  • 230
  • 2
    I can see you downvoting, but I think you got it backwards. I've done this before on an Intel system. – Robert Harvey Sep 23 '10 at 22:18
  • This method is returning negative values for me. Any idea why? – Codecat Sep 23 '10 at 22:20
  • 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? – Codecat Sep 23 '10 at 22:23
  • @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 Sep 23 '10 at 22:29
  • Hmm. Confused here. It returns the correct integer value for all files I feed it. – Codecat Sep 23 '10 at 22:29
  • @Timwi: What's wrong is that your code is little-endian, just like the data, but you keep calling it big-endian. – Steven Sudit Sep 23 '10 at 22:30
  • 1
    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 Sep 23 '10 at 22:32
  • Go read http://en.wikipedia.org/wiki/Endianness until you understand the difference. – Steven Sudit Sep 23 '10 at 22:33
  • @Robert, @0xA3, @Steven: You were right, I got the terminology wrong, and `BinaryReader` does do it. I have corrected the answer accordingly. – Timwi Sep 23 '10 at 22:33