Чтение байтового массива в тип / переменную в упрощенном виде? - PullRequest
2 голосов
/ 07 июня 2011

У меня есть соединение через сокет TCP, где я получаю байтовый массив данных, который мне нужно расшифровать, а затем отформатировать, поэтому я ищу простой способ чтения этого результирующего байтового массива, с помощью которого я могу легко пройти через элементы, экспортирующие его какМне нужен тип или переменная и т. Д., Чтобы объяснить это лучше, см. Следующий пример:

Допустим, у нас есть байтовый массив (пакеты с прямым порядком байтов, это просто гипотетический пример) :

01 02 00 03 74 00 74 00 69 00

Я хочу поместить первые 2 байта в целое число, следующие 2 байта в другое целое число и оставить его в виде строки.

Так что это будет выглядеть так:

int first = 258;
int seconnd = 3;
string rest = "tti"

Я не Java-человек, но на Java было кое-что интересное, я заметил, что где-то люди читают пакеты прямо так:

messageSize = readH(); // for 2 bytes
key = readH(); // for 2 bytes
message = readS(); // read as string

И мне было интересно, есть ли подобноеготовая функция в C # или любой другой ресурс, который может помочь мне лучше справиться с этой задачей?

Ответы [ 2 ]

2 голосов
/ 07 июня 2011

Вы можете использовать BinaryReader следующим образом:

using (var reader = new BinaryReader(stream))
{
    var first = reader.ReadUInt16();
    var second = reader.ReadUInt16();
    var stringBytes = reader.ReadBytes(6);
    var str = Encoding.Unicode.GetString(stringBytes);
}

Тем не менее, это будет работать, только если используется метод с прямым порядком байтов.

Пример, который вы разместили, - это big-endian.

Я предполагаю, что вы реализуете и писатель, и отправитель в C #, так что вы можете использовать BinaryReader и BinaryWriter, они оба используют little-endian, так что они будут понимать друг друга.

[Редактировать]

Другой подход, который вы могли бы рассмотреть, - это struct.
Например, в вашем случае:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyStruct
{
    public ushort First;
    public ushort Second;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string MyString;
}

Кодбудет выглядеть так

var myStruct = new MyStruct { First = 1, Second = 2, MyString = "asd" };

var bytes = StructToBytes(myStruct);

var myStruct1 = BytesToStruct<MyStruct>(bytes);

и два служебных метода:

public static T BytesToStruct<T>(byte[] bytes) where T : struct
{
    AssertUtilities.ArgumentNotNull(bytes, "bytes");

    var structSize = Marshal.SizeOf(typeof(T));
    var pointer = IntPtr.Zero;
    try
    {
        pointer = Marshal.AllocHGlobal(structSize);
        Marshal.Copy(bytes, 0, pointer, structSize);
        return (T)Marshal.PtrToStructure(pointer, typeof(T));
    }
    finally
    {
        if (pointer != IntPtr.Zero)
            Marshal.FreeHGlobal(pointer);
    }
}

public static byte[] StructToBytes<T>(T structObject) where T : struct
{
    var structSize = Marshal.SizeOf(typeof(T));
    var bytes = new byte[structSize];
    var pointer = IntPtr.Zero;
    try
    {
        pointer = Marshal.AllocHGlobal(structSize);
        Marshal.StructureToPtr(structObject, pointer, true);
        Marshal.Copy(pointer, bytes, 0, structSize);
        return bytes;
    }
    finally
    {
        if (pointer != IntPtr.Zero)
            Marshal.FreeHGlobal(pointer);
    }
}
1 голос
/ 07 июня 2011

Для простых примеров вы можете сделать что-то вроде:

static class StreamHelpers
{
    public static int ReadH(this Stream stream)
    {
        int x = stream.ReadByte(), y = stream.ReadByte();
        if (x < 0 || y < 0) throw new EndOfStreamException();
        return (y << 8) | x;
    }
    ...

}

, который дает вам доступ к:

int i = stream.ReadH();

и т.д.. Однако, лично Я бы написал пользовательский blahReader (для вашего blah) с внутренним буфером byte[] и курсором отслеживания. Это позволяет must более эффективно читать в большинстве случаев (даже по сравнению с BufferedStream).

Как примечание, ваша строка выглядит как UTF-16 и читается в EOF; чтение в EOF - это боль, так как оно не позволяет вам кодировать 2 строк. Я бы (вместо этого) добавил (сначала) длину строки, и я бы использовал UTF-8, если нет большой вероятности больших символов. Тогда чтение строки имеет вид:

  • читать длину
  • расшифруйте столько через Encoding.*

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

...