8

I am trying to read a binary file with the BinaryReader class, and I need to read it in as blocks of UInt32, and then do some bit shifting etc. afterwords.

But, for some reason bit order is reversed when I use the ReadUInt32 method.

If I for example have a file where the first four bytes looks like this in hex, 0x12345678, they end up like this after being read by ReadUInt32: 0x78563412.

If I use the ReadBytes(4) method, I get the expected array:

[0x00000000]    0x12    byte
[0x00000001]    0x34    byte
[0x00000002]    0x56    byte
[0x00000003]    0x78    byte

Why is this? Is it just the way .net represents uints in memory? Is it the same across the different platforms (I am running 64bit Windows 7, .net 3.5 sp1)?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Egil Hansen
  • 15,028
  • 8
  • 37
  • 54
  • Can you quell our curiosity by telling us how you fixed it? :) – Colin Burnett May 23 '09 at 02:06
  • 1
    Of course :) In reality it do not matter which way the byte order is, as long as it is consistent across platfroms (x64, x86), I can still extract the bits I need, I just have to change my bit shifting. As far as I can see, uint is in general stored as little-endian, not just the uint build by ReadUInt32, so that makes everything easier. – Egil Hansen May 23 '09 at 08:27

6 Answers6

10

Yes, this has to do with how your computer hardware stores uints in memory. It can be different across different platforms, although most desktop computers should be the same.

This is called endianness -- see wikipedia here:

http://en.wikipedia.org/wiki/Endian

Clyde
  • 8,017
  • 11
  • 56
  • 87
8

This seems to be an endianness issue. The docs say ReadUint32 reads in little-endian so the first byte is the least-significant so it goes to the lowest memory location. Your writer must be big-endian?

BinaryWriter.Write(UInt32) says it writes little-endian too. Is your binary data source not BinaryWriter?

Essentially what you need to do to fix it is this:

uint a = 0x12345678;
uint b = ((a & 0x000000FF) << 24) + ((a & 0x0000FF00) << 8) + ((a & 0x00FF0000) >> 8) + ((a & 0xFF000000) >> 24);

This shifts the least-significant byte up 24 bits, the 2nd LSB up 8 bits, the 3rd LSB down 8 bits, and the 4th LSB (the MSB) down 24 bits. Doing this is covered in several libraries.

Perhaps using BitConverter would be a bit more clear:

uint a = 0x12345678;
byte[] bytes = BitConverter.GetBytes(a);
// Swap byte order
uint b = BitConverter.ToUInt32(new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] }, 0);
Colin Burnett
  • 11,150
  • 6
  • 31
  • 40
3

Look into Jon Skeet's MiscUtil library for the Endian* classes, like EndianBinaryReader and EndianBitConverter.

http://www.yoda.arachsys.com/csharp/miscutil/

bsneeze
  • 4,369
  • 25
  • 20
2

Jon Skeet's written a BitConverter with configurable endian-ness. You might find it useful.

http://www.yoda.arachsys.com/csharp/miscutil/

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
1

This is an issue of platform Endianess. When you read data from a stream you must read it accordingly to the endianess it was written as. If you created the data in .Net, then .Net will read it correctly.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
0

Read Generic BinaryReader and BinaryWriter Extensions, a great way to handle generic casting the unmanaged way.

For VB.NET (safe code only, can also be achieved in C#) use the following:

Imports System.IO Imports System.Runtime.CompilerServices Imports System.Runtime.InteropServices

<HideModuleName()>
Public Module BinaryReaderExtensions

 <Extension()>
 Public Function Read(Of T As Structure)(br As BinaryReader) As T
  Dim bytes = br.ReadBytes(Marshal.SizeOf(GetType(T)))
  Dim handle = GCHandle.Alloc(bytes, GCHandleType.Pinned)
  Return Marshal.PtrToStructure(handle.AddrOfPinnedObject, GetType(T))
 End Function

 <Extension()>
 Public Function ReadReverse(Of T As Structure)(br As BinaryReader) As T
  Dim bytes = br.ReadBytes(Marshal.SizeOf(GetType(T))).Reverse.ToArray
  Dim handle = GCHandle.Alloc(bytes, GCHandleType.Pinned)
  Return Marshal.PtrToStructure(handle.AddrOfPinnedObject, GetType(T))
 End Function

End Module

You can now implement the same functionality for BitConverter, for BinaryWriter etc.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632