我在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);
该转换还有其他方法吗?
答案 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();