Почему BinaryReader.ReadUInt32 () обращает битовую комбинацию? - PullRequest
7 голосов
/ 22 мая 2009

Я пытаюсь прочитать двоичный файл с помощью класса BinaryReader, и мне нужно прочитать его как блоки UInt32, а затем выполнить некоторое сдвиг битов и т. Д. После слов.

Но по какой-то причине порядок следования битов меняется, когда я использую метод ReadUInt32.

Если у меня, например, есть файл, в котором первые четыре байта выглядят так в шестнадцатеричном виде, то 0x12345678, они заканчиваются следующим образом после чтения ReadUInt32: 0x78563412.

Если я использую метод ReadBytes (4), я получаю ожидаемый массив:

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

Почему это? Это просто способ .net представляет Uints в памяти? Это одинаково для разных платформ (у меня 64-битная Windows 7, .net 3.5 sp1)?

Ответы [ 6 ]

8 голосов
/ 22 мая 2009

Кажется, это проблема endianness . В документах говорится, что ReadUint32 читает с прямым порядком байтов, поэтому первый байт является наименее значимым, поэтому он идет в самое низкое место в памяти. Ваш писатель, должно быть, с прямым порядком байтов?

BinaryWriter.Write(UInt32) говорит, что пишет также little-endian. Ваш двоичный источник данных не BinaryWriter?

По сути, вам нужно сделать следующее:

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

Это сдвигает младший значащий байт на 24 бита, 2-й младший бит на 8 бит, 3-й младший младший 8 бит и 4-й младший бит (MSB) на 24 бит. Это описано в нескольких библиотеках.

Возможно, использование BitConverter будет более понятным:

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);
8 голосов
/ 22 мая 2009

Да, это связано с тем, как ваше компьютерное оборудование хранит информацию в памяти. На разных платформах он может быть разным, хотя большинство настольных компьютеров должны быть одинаковыми.

Это называется порядком байтов - смотрите википедию здесь:

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

3 голосов
/ 22 мая 2009

Просмотрите библиотеку Джона Скита MiscUtil для классов * Endian, таких как EndianBinaryReader и EndianBitConverter.

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

2 голосов
/ 22 мая 2009

Джон Скит написал BitConverter с настраиваемым порядком байтов. Вы можете найти это полезным.

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

1 голос
/ 22 мая 2009

Это проблема платформы Endianess . Когда вы читаете данные из потока, вы должны читать их в соответствии с порядком, в котором они были записаны. Если вы создали данные в .Net, то .Net прочитает их правильно.

0 голосов
/ 18 июля 2010

Чтение Универсальные расширения BinaryReader и BinaryWriter , отличный способ обработки обобщенного приведения неуправляемым способом.

Для VB.NET (только безопасный код, также может быть достигнут в C #) используйте следующее:

Импорт System.IO Импортирует System.Runtime.CompilerServices Импортирует 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

Теперь вы можете реализовать те же функции для BitConverter, для BinaryWriter и т. Д.

...