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

问题描述 投票:25回答:9

我在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);

该转换还有其他方法吗?

c# bytearray endianness
9个回答
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)。


34
投票

在现代的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);

:)


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;
} 

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...

3
投票

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

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

它支持有符号和无符号整数(byte / short / int / long)。


1
投票

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

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

1
投票

您还可以使用Jon Skeet“Misc Utils”库,可在https://jonskeet.uk/csharp/miscutil/获得

他的图书馆有许多实用功能。对于Big / Little端转换,您可以查看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年开始的,但我想这仍然是相关的。


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);
}

0
投票

我不喜欢BitConverter,因为(正如Marc Gravell所说),它被认为依赖于系统字节序,这意味着你每次使用BitConverter时都必须在技术上进行系统字节序检查,以确保你不必反转数组。通常,对于保存的文件,您通常知道您尝试阅读的字节序,并且可能不一样。您可能只是处理具有big-endian值的文件格式,例如,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个字节之间的任何值,包括little-endian和big-endian。唯一的小用法特性是你需要给出要读取的字节数,并且需要专门将结果转换为你想要的类型。

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

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传递给函数时,必须将输入arg转换为64位无符号整数。

© www.soinside.com 2019 - 2024. All rights reserved.