Подтвердить что ты не робот

Как получить маленькие конечные данные из большого эндиана в С# с помощью метода bitConverter.ToInt32?

Я делаю приложение в С#. В этом приложении у меня есть массив байтов, содержащий шестнадцатеричные значения.

Здесь я получаю данные как большой endian, но я хочу, чтобы это было немного endian.

Здесь я использую метод Bitconverter.toInt32 для преобразования этого значения в integer.

Но моя проблема в том, что перед преобразованием значения мне нужно скопировать данные из 4 байтов во временный массив из исходного байтового массива и затем отменить этот временный массив байтов.

Я наклоняю массив обратного источника, потому что он содержит и другие данные.

Из-за этого мое приложение становится медленным. code Здесь у меня есть один исходный массив байта как 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);

Есть ли какой-либо другой метод для этого преобразования?

4b9b3361

Ответ 1

Если вы знаете, что данные являются большими, возможно, просто сделайте это вручную:

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

это также будет надежно работать на любом процессоре. Примечание 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;

Я считаю, что первое очень читаемое, и нет ветвей/сложного кода, поэтому он должен работать довольно быстро. Второй может также столкнуться с проблемами на некоторых платформах (где ЦП уже работает по-крупному).

Ответ 2

В современном 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);

:)

Ответ 3

Ну вот

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

Ответ 4

Объявите этот класс:

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

Ответ 5

Добавьте ссылку на System.Memory nuget и используйте BinaryPrimitives.ReverseEndianness().

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

Он поддерживает как подписанные, так и целые числа без знака (byte/short/int/long).

Ответ 6

Если вам больше не понадобится этот обратный временный массив, вы можете просто создать его при передаче параметра вместо четырех назначений. Например:

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

Ответ 7

Я использую следующие вспомогательные функции

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

Мне не нравится BitConverter, потому что (как ответил Марк Гравелл) он вынужден полагаться на системную сущность, то есть вам технически приходится выполнять проверку системной проверки каждый раз, когда вы используете BitConverter чтобы убедиться, что вам не нужно BitConverter массив. Как правило, с сохраненными файлами вы обычно знаете конечную цель, которую вы пытаетесь прочитать, и это может быть не то же самое. Вы можете просто обрабатывать форматы файлов с большими значениями, например, например, фрагменты PNG.

Из-за этого я просто написал для себя свои собственные методы, которые принимают байтовый массив, смещение чтения и длину чтения в качестве аргументов, а также логическое значение для указания обработки endianness и которое использует смещение битов для эффективности:

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

Единственное требование здесь состоит в том, что вам нужно передать входной аргумент в 64-разрядное целое число без знака при передаче его функции.