如何使用bitConverter.ToInt32方法从c#中获取big endian的小端数据?

时间:2011-11-23 11:02:54

标签: c# bytearray endianness

我在c#中创建应用程序。在该应用程序中,我有包含十六进制值的字节数组。

在这里,我将数据作为一个大端,但我希望它是一个小端。

这里我使用Bitconverter.toInt32方法将该值转换为整数。

但我的问题是,在转换值之前,我必须将该4字节数据从源字节数组复制到临时数组中,然后反转该临时字节数组。

我无法反转源数组,因为它还包含其他数据。

因为我的申请变慢了。 代码这里我有一个字节的源数组作为waveData []。它包含很多数据。

byte[] tempForTimestamp=new byte[4];
tempForTimestamp[0] = waveData[290];
tempForTimestamp[1] = waveData[289];
tempForTimestamp[2] = waveData[288];
tempForTimestamp[3] = waveData[287];
int number = BitConverter.ToInt32(tempForTimestamp, 0);

该转换还有其他方法吗?

12 个答案:

答案 0 :(得分:31)

在现代的Linq中,单行和易于理解的版本将是:

int number = BitConverter.ToInt32(waveData.Skip(286).Take(4).Reverse().ToArray(), 0);

你也可以......

byte[] tempForTimestamp = new byte[4];
Array.Copy(waveData, 287, tempForTimestamp, 0, 4);
Array.Reverse(tempForTimestamp);
int number = BitConverter.ToInt32(tempForTimestamp);

:)

答案 1 :(得分:23)

如果您知道数据是big-endian,也许只需手动执行:

int value = (buffer[i++] << 24) | (buffer[i++] << 16)
          | (buffer[i++] << 8) | buffer[i++];

这也可以在任何CPU上可靠地工作。注意i是您当前到缓冲区的偏移量。

另一种方法是改组数组:

byte tmp = buffer[i+3];
buffer[i+3] = buffer[i];
buffer[i] = tmp;
tmp = buffer[i+2];
buffer[i+2] = buffer[i+1];
buffer[i+1] = tmp;
int value = BitConverter.ToInt32(buffer, i);
i += 4;

我发现第一个更具可读性,并且没有分支/复杂代码,所以它也应该非常快。第二个也可能在某些平台上遇到问题(CPU已经在运行big-endian)。

答案 2 :(得分:11)

你去吧

public static int SwapEndianness(int value)
{
    var b1 = (value >> 0) & 0xff;
    var b2 = (value >> 8) & 0xff;
    var b3 = (value >> 16) & 0xff;
    var b4 = (value >> 24) & 0xff;

    return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
} 

答案 3 :(得分:8)

声明此类:

using static System.Net.IPAddress;

namespace BigEndianExtension
{
    public static class BigEndian
    {
        public static short ToBigEndian(this short value)   => HostToNetworkOrder(value);
        public static int   ToBigEndian(this int value)     => HostToNetworkOrder(value);
        public static long  ToBigEndian(this long value)    => HostToNetworkOrder(value);
        public static short FromBigEndian(this short value) => NetworkToHostOrder(value);
        public static int   FromBigEndian(this int value)   => NetworkToHostOrder(value);
        public static long  FromBigEndian(this long value)  => NetworkToHostOrder(value);
    }
}

示例,使用按钮和多行文本框创建表单:

using BigEndianExtension;

private void button1_Click(object sender, EventArgs e)
{
    short int16 = 0x1234;
    int int32   = 0x12345678;
    long int64  = 0x123456789abcdef0;
    string text = string.Format("LE:{0:X4}\r\nBE:{1:X4}\r\n", int16, int16.ToBigEndian());

    text += string.Format("LE:{0:X8}\r\nBE:{1:X8}\r\n", int32, int32.ToBigEndian());
    text += string.Format("LE:{0:X16}\r\nBE:{1:X16}\r\n", int64, int64.ToBigEndian());
    textBox1.Text = text;
}
//Some code...

答案 4 :(得分:3)

添加对System.Memory nuget的引用,并使用BinaryPrimitives.ReverseEndianness()。

using System.Buffers.Binary;
number = BinaryPrimitives.ReverseEndianness(number);

它支持有符号和无符号整数(字节/短/整数/长)。

答案 5 :(得分:1)

如果你不再需要那个反向的临时数组,你可以在传递参数时创建它,而不是进行四次分配。例如:

int i = 287;
int value = BitConverter.ToInt32({
    waveData(i + 3),
    waveData(i + 2),
    waveData(i + 1),
    waveData(i)
}, 0);

答案 6 :(得分:1)

您还可以使用https://jonskeet.uk/csharp/miscutil/上的Jon Skeet“其他实用程序”库

他的库具有许多实用程序功能。对于大/小尾数转换,您可以检查MiscUtil/Conversion/EndianBitConverter.cs文件。

var littleEndianBitConverter = new MiscUtil.Conversion.LittleEndianBitConverter();
littleEndianBitConverter.ToInt64(bytes, offset);

var bigEndianBitConverter = new MiscUtil.Conversion.BigEndianBitConverter();
bigEndianBitConverter.ToInt64(bytes, offset);

他的软件来自2009年,但我想它仍然很重要。

答案 7 :(得分:0)

我使用以下帮助函数

public static Int16 ToInt16(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt16(BitConverter.IsLittleEndian ? data.Skip(offset).Take(2).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt16(data, offset);
}
public static Int32 ToInt32(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt32(BitConverter.IsLittleEndian ? data.Skip(offset).Take(4).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt32(data, offset);
}
public static Int64 ToInt64(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt64(BitConverter.IsLittleEndian ? data.Skip(offset).Take(8).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt64(data, offset);
}

答案 8 :(得分:0)

我不喜欢BitConverter,因为(正如Marc Gravell回答的那样)它被认为依赖于系统字节序,这意味着从技术上来说,每次使用BitConverter时都必须进行系统字节序检查,以确保您不会不必反转数组。通常,对于保存的文件,您通常都知道要读取的字节序,而这可能并不相同。您可能也只在处理具有大端值的文件格式,例如PNG块。

因此,我为此编写了自己的方法,该方法使用字节数组,读取偏移量和读取长度作为参数,以及一个布尔值以指定字节序处理,并且使用移位来提高效率:

public static UInt64 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to read a " + bytes + "-byte value at offset " + startIndex + ".");
    UInt64 value = 0;
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        value += (UInt64)(data[offs] << (8 * index));
    }
    return value;
}

此代码可以处理1到8个字节之间的任何值,小端和大端。唯一的小用法是,您需要同时提供要读取的字节数,都需要将结果专门转换为所需的类型。

示例代码中,我用它来读取某些专有图像类型的标题:

Int16 imageWidth = (Int16) ReadIntFromByteArray(fileData, hdrOffset, 2, true);
Int16 imageHeight = (Int16) ReadIntFromByteArray(fileData, hdrOffset + 2, 2, true);

这将从数组中读取两个连续的16位整数,作为有符号的little-endian值。当然,您可以为所有可能的情况制作一堆重载函数,如下所示:

public Int16 ReadInt16FromByteArrayLe(Byte[] data, Int32 startIndex)
{
    return (Int16) ReadIntFromByteArray(data, startIndex, 2, true);
}

但是我个人并没有为此而烦恼。

而且,写入字节也是如此:

public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt64 value)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        data[offs] = (Byte) (value >> (8*index) & 0xFF);
    }
}

这里唯一的要求是,在将输入arg传递给函数时,必须将其转换为64位无符号整数。

答案 9 :(得分:0)

public static unsafe int Reverse(int value)
{
    byte* p = (byte*)&value;
    return (*p << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}

如果允许不安全...根据Marc Gravell的帖子

答案 10 :(得分:0)

最直接的方法是使用.NET Standard 2.1中引入的BinaryPrimitives.ReadInt32BigEndian(ReadOnlySpan) Method

var number = BinaryPrimitives.ReadInt32BigEndian(waveData[297..291]);

答案 11 :(得分:0)

如果允许使用不安全的代码,这将反转数据内联...

fixed (byte* wavepointer = waveData)
   new Span<byte>(wavepointer + offset, 4).Reverse();