【发布时间】:2009-05-18 20:42:19
【问题描述】:
我正在解析二进制文件格式。它以自然适合 c# 的 uint 类型的方式使用四个字节对整数进行编码。
实现此功能的最 C#/惯用方式是什么:
uint ReadUint(byte[] buffer);
假设缓冲区包含 4 个元素。完整的答案可能会考虑文件中由小/大端假设引起的一些常见字节排序,并记录它选择解析的那些。
【问题讨论】:
我正在解析二进制文件格式。它以自然适合 c# 的 uint 类型的方式使用四个字节对整数进行编码。
实现此功能的最 C#/惯用方式是什么:
uint ReadUint(byte[] buffer);
假设缓冲区包含 4 个元素。完整的答案可能会考虑文件中由小/大端假设引起的一些常见字节排序,并记录它选择解析的那些。
【问题讨论】:
最基本的(但有点危险的re endianness)是:
return BitConverter.ToUInt32(buffer, 0);
除此之外,移位也可以(根据您自己的回复)- 或者您可以使用 Jon 的 EndianBitConverter in MiscUtil,它负责处理翻译。
(编辑)
我在 protobuf-net 中使用的 little-endian 位移版本与您的版本几乎相同 - 我只是按升序阅读它们并使用按位(非数字)加法:
return ((uint)buffer[0])
| (((uint)buffer[1]) << 8)
| (((uint)buffer[2]) << 16)
| (((uint)buffer[3]) << 24);
【讨论】:
我通常会为此使用 BitConverter 类。在您的情况下,BitConverter.ToUInt32() 方法。
【讨论】:
这个回复实际上是一个扩展评论(因此是 wiki),比较了 BitConverter 的性能和使用 + 与 | 的位移;它仅适用于微优化!
结果第一:
BitConverter: 972ms, chk=1855032704
Bitwise: 740ms, chk=1855032704
ReadLength: 1316ms, chk=1855032704
或者如果调整为允许非零基偏移量的结果:
BitConverter: 905ms, chk=1855032704
Bitwise: 1058ms, chk=1855032704
ReadLength: 1244ms, chk=1855032704
还有代码:
using System;
using System.Diagnostics;
static class Program
{
static void Main()
{
byte[] buffer = BitConverter.GetBytes((uint)123);
const int LOOP = 50000000;
uint chk = 0;
var watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
chk += BitConverter.ToUInt32(buffer, 0);
}
watch.Stop();
Console.WriteLine("BitConverter: " + watch.ElapsedMilliseconds
+ "ms, chk=" + chk);
chk = 0;
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
chk += Bitwise(buffer);
}
watch.Stop();
Console.WriteLine("Bitwise: " + watch.ElapsedMilliseconds
+ "ms, chk=" + chk);
chk = 0;
watch = Stopwatch.StartNew();
for (int i = 0; i < LOOP; i++)
{
chk += ReadLength(buffer);
}
watch.Stop();
Console.WriteLine("ReadLength: " + watch.ElapsedMilliseconds
+ "ms, chk=" + chk);
Console.ReadKey();
}
static uint Bitwise(byte[] buffer)
{
return ((uint)buffer[0])
| (((uint)buffer[1]) << 8)
| (((uint)buffer[2]) << 16)
| (((uint)buffer[3]) << 24);
}
static uint ReadLength(byte[] buffer)
{
uint result = ((uint)buffer[3]) << 24;
result += ((uint)buffer[2]) << 16;
result += ((uint)buffer[1]) << 8;
result += buffer[0];
return result;
}
}
【讨论】:
作为一个来自 C 的人,这就是我目前实现此功能的方式:
static uint ReadLength(byte[] buffer)
{
uint result = ((uint) buffer[3]) << 24;
result |= ((uint) buffer[2]) << 16;
result |= ((uint) buffer[1]) << 8;
result |= buffer[offset];
return result;
}
这会解析维基百科声称在 i386/Vista 上运行的 .net 实现上以 little-endian 方式布局的格式
【讨论】:
byte[] ba = new byte[]{ 0x10, 0xFF, 0x11, 0x01 } ;
var ui = BitConverter.ToUInt32(ba, 0);
【讨论】:
最简单的方法就是
int val = System.BitConverter.ToInt32(buffer, 0);
这使用当前系统字节序,这可能是也可能不是您想要的。
【讨论】:
假设您想读取它们的流(正如您的代码所建议的那样) 我会说这非常接近事实上的标准方式:
MemoryStream ms = new MemoryStream(new byte[100]);
BinaryReader br = new BinaryReader(ms);
uint q = br.ReadUInt32();
【讨论】: