C # - двоичный ридер в Big Endian? - PullRequest
27 голосов
/ 24 декабря 2011

Я пытаюсь улучшить мое понимание формата файлов STFS, используя программу для считывания всех различных битов информации.Используя веб-сайт со ссылкой на то, какие смещения содержат какую информацию, я написал некоторый код с двоичным считывателем, просмотрите файл и поместите значения в правильные переменные.

Проблема в том, что все данные ПРЕДЛОЖЕНЫбыть Big Endian, и все, что читал двоичный читатель, - Little Endian.Итак, как лучше всего это исправить?

Могу ли я создать имитирующий класс Binary reader, который возвращает обращенный массив байтов?Есть ли что-то, что я могу изменить в экземпляре класса, чтобы он читался с прямым порядком байтов, поэтому мне не нужно переписывать все?

Любая помощь приветствуется.

edit: я пытался добавить Encoding.BigEndianUnicode в качестве параметра, но он по-прежнему читается с прямым порядком байтов.

Ответы [ 4 ]

36 голосов
/ 24 декабря 2011

Обычно я не отвечаю на свои вопросы, но я достиг именно того, чего хотел, с помощью простого кода:

class BinaryReader2 : BinaryReader { 
    public BinaryReader2(System.IO.Stream stream)  : base(stream) { }

    public override int ReadInt32()
    {
        var data = base.ReadBytes(4);
        Array.Reverse(data);
        return BitConverter.ToInt32(data, 0);
    }

    public Int16 ReadInt16()
    {
        var data = base.ReadBytes(2);
        Array.Reverse(data);
        return BitConverter.ToInt16(data, 0);
    }

    public Int64 ReadInt64()
    {
        var data = base.ReadBytes(8);
        Array.Reverse(data);
        return BitConverter.ToInt64(data, 0);
    }

    public UInt32 ReadUInt32()
    {
        var data = base.ReadBytes(4);
        Array.Reverse(data);
        return BitConverter.ToUInt32(data, 0);
    }

}

Я знал, что это то, что я хотел, но я не знал, как это написать. Я нашел эту страницу, и она помогла: http://www.codekeep.net/snippets/870c4ab3-419b-4dd2-a950-6d45beaf1295.aspx

11 голосов
/ 07 марта 2013

ИМХО, ответ немного лучше, так как он не требует нового класса для обновления, делает вызовы с прямым порядком байтов очевидными и позволяет смешивать вызовы с большим и меньшим порядком байтов в потоке.

public static class Helpers
{
  // Note this MODIFIES THE GIVEN ARRAY then returns a reference to the modified array.
  public static byte[] Reverse(this byte[] b)
  {
    Array.Reverse(b);
    return b;
  }

  public static UInt16 ReadUInt16BE(this BinaryReader binRdr)
  {
    return BitConverter.ToUInt16(binRdr.ReadBytesRequired(sizeof(UInt16)).Reverse(), 0);
  }

  public static Int16 ReadInt16BE(this BinaryReader binRdr)
  {
    return BitConverter.ToInt16(binRdr.ReadBytesRequired(sizeof(Int16)).Reverse(), 0);
  }

  public static UInt32 ReadUInt32BE(this BinaryReader binRdr)
  {
    return BitConverter.ToUInt32(binRdr.ReadBytesRequired(sizeof(UInt32)).Reverse(), 0);
  }

  public static Int32 ReadInt32BE(this BinaryReader binRdr)
  {
    return BitConverter.ToInt32(binRdr.ReadBytesRequired(sizeof(Int32)).Reverse(), 0);
  }

  public static byte[] ReadBytesRequired(this BinaryReader binRdr, int byteCount)
  {
    var result = binRdr.ReadBytes(byteCount);

    if (result.Length != byteCount)
      throw new EndOfStreamException(string.Format("{0} bytes required from stream, but only {1} returned.", byteCount, result.Length));

    return result;
  }
}
7 голосов
/ 24 декабря 2011

Я не знаком с STFS, но изменение порядка байтов относительно просто.«Сетевой порядок» имеет большой порядковый номер, поэтому все, что вам нужно сделать, это перевести из сети в порядок хостов.

Это легко, потому что уже есть код, который делает это.Посмотрите на IPAddress.NetworkToHostOrder, как объяснено здесь: ntohs () и ntohl () эквивалентны?

5 голосов
/ 06 февраля 2014

По-моему, вы хотите быть осторожными при этом.Причина, по которой можно захотеть конвертировать из BigEndian в LittleEndian, заключается в том, что считываемые байты находятся в BigEndian, а ОС, рассчитывающая их, работает в LittleEndian.

C # больше не является языком только для окон.С такими портами, как Mono, а также с другими платформами Microsoft, такими как Windows Phone 7/8, Xbox 360 / Xbox One, Windwos CE, Windows 8 Mobile, Linux с MONO, Apple с MONO и т. Д. Вполне возможно, что операционная платформа может быть вBigEndian, и в этом случае вы бы сами себя испортили, если преобразовали код без каких-либо проверок.

В BitConverter уже есть поле под названием «IsLittleEndian», которое можно использовать, чтобы определить, является ли операционная средав LittleEndian или нет.Затем вы можете сделать реверсирование условно.

Поэтому я просто написал несколько расширений byte [] вместо создания большого класса:

    /// <summary>
    /// Get's a byte array from a point in a source byte array and reverses the bytes. Note, if the current platform is not in LittleEndian the input array is assumed to be BigEndian and the bytes are not returned in reverse order
    /// </summary>
    /// <param name="byteArray">The source array to get reversed bytes for</param>
    /// <param name="startIndex">The index in the source array at which to begin the reverse</param>
    /// <param name="count">The number of bytes to reverse</param>
    /// <returns>A new array containing the reversed bytes, or a sub set of the array not reversed.</returns>
    public static byte[] ReverseForBigEndian(this byte[] byteArray, int startIndex, int count)
    {
        if (BitConverter.IsLittleEndian)
            return byteArray.Reverse(startIndex, count);
        else
            return byteArray.SubArray(startIndex, count);

    }

    public static byte[] Reverse(this byte[] byteArray, int startIndex, int count)
    {
        byte[] ret = new byte[count];
        for (int i = startIndex + (count - 1); i >= startIndex; --i)
        {
            byte b = byteArray[i];
            ret[(startIndex + (count - 1)) - i] = b;
        }
        return ret;
    }

    public static byte[] SubArray(this byte[] byteArray, int startIndex, int count)
    {
        byte[] ret = new byte[count];
        for (int i = 0; i < count; ++i)            
            ret[0] = byteArray[i + startIndex];
        return ret;
    }

Итак, представьте пример кода:

byte[] fontBytes = byte[240000]; //some data loaded in here, E.G. a TTF TrueTypeCollection font file. (which is in BigEndian)

int _ttcVersionMajor = BitConverter.ToUint16(fontBytes.ReverseForBigEndian(4, 2), 0);

//output
_ttcVersionMajor = 1 //TCCHeader is version 1
...