Как получить данные с прямым порядком байтов из Big Endian в C # с помощью метода bitConverter.ToInt32? - PullRequest
27 голосов
/ 23 ноября 2011

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

Здесь я получаю данные с прямым порядком байтов, но я хочу, чтобы они были с прямым порядком байтов.

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

Но моя проблема в том, что перед преобразованием значения мне нужно скопировать эти 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);

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

Ответы [ 9 ]

34 голосов
/ 18 марта 2013

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

:)

23 голосов
/ 23 ноября 2011

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

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;

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

11 голосов
/ 23 ноября 2011

Вот, пожалуйста,

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 голосов
/ 23 мая 2014

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

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 голосов
/ 22 июля 2018

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

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

Поддерживаются целые числа со знаком и без знака (байты / короткие / целые / длинные).

1 голос
/ 27 марта 2019

Вы также можете использовать библиотеку 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 года, но я думаю, оно все еще актуально.

1 голос
/ 18 мая 2015

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

int i = 287;
int value = BitConverter.ToInt32({
    waveData(i + 3),
    waveData(i + 2),
    waveData(i + 1),
    waveData(i)
}, 0);
0 голосов
/ 04 декабря 2018

Мне не нравится BitConverter, потому что (как ответил Марк Гравелл) предполагается, что он полагается на системный порядок байтов, то есть технически вы должны выполнять проверку системного порядкового номера каждый раз, когда используете 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-битных целых числа из массива в виде значений со знаком с прямым порядком байтов.Конечно, вы можете просто сделать несколько функций перегрузки для всех возможностей, например:

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-битному целому числу без знака при передаче его функции.

0 голосов
/ 23 ноября 2017

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

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...