为什么BinaryReader.ReadUInt32()方法会颠倒比特模式?

8

我正在尝试使用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)?


你能告诉我们你是如何修复它的,以满足我们的好奇心吗? :) - Colin Burnett
1
当然可以 :) 实际上字节顺序无论是哪种方式都没关系,只要在平台(x64、x86)上保持一致性,我仍然可以提取所需的位,只需要改变我的位移即可。就我所看到的,uint 通常被存储为小端,不仅适用于 ReadUInt32 构建的 uint,这使得所有事情都变得更加容易。 - Egil Hansen
6个回答

10

是的,这与您计算机硬件在内存中存储uints有关。虽然大多数台式机应该相同,但在不同平台上可能会有所不同。

这被称为字节序 - 请参见维基百科这里:

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


8
这似乎是一个字节序问题,endianness。根据文档,ReadUint32按小端读取,因此第一个字节是最不重要的字节,所以它被写入最低的内存位置。你的writer必须是大端的吧? BinaryWriter.Write(UInt32) 说它也是写小端的。你的二进制数据源不是BinaryWriter吗?
基本上,你需要做的是这样的:
uint a = 0x12345678;
uint b = ((a & 0x000000FF) << 24) + ((a & 0x0000FF00) << 8) + ((a & 0x00FF0000) >> 8) + ((a & 0xFF000000) >> 24);

这将把最不重要的字节向左移动24位,第二个最不重要的字节向左移动8位,第三个最不重要的字节向右移动8位,第四个最不重要的字节(也就是最重要的字节)向右移动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);

3

2

1

这是一个平台字节序的问题。当你从流中读取数据时,必须按照写入时的字节序进行读取。如果你是在 .Net 中创建的数据,则 .Net 将正确地读取它。


1
哈哈,不到1分钟就能找到3个维基百科链接。这真应该有一个徽章! - Remus Rusanu

0

阅读 通用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

现在您可以为 BitConverterBinaryWriter 等实现相同的功能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接