Считывание строки из массива байтов (не преобразование массива байтов в строку)

StackOverflow https://stackoverflow.com/questions/492454

Вопрос

У меня есть массив байтов, который я считываю из сетевого потока.Первые два байта указывают длину следующего пакета, а затем пакет считывается в массив байтов этой длины.Данные, которые мне нужно прочитать из массива NetworkStream / byte, содержат несколько строк, т. е.данные переменной длины заканчиваются символами новой строки и некоторыми полями фиксированной ширины, такими как bytes и longs.Итак, что-то вроде этого:

// I would have delimited these for clarity but I didn't want
// to imply that the stream was delimited because it's not.
StringbyteStringStringbytebytebytelonglongbytelonglong

Я знаю (и имею право высказаться) формат пакета данных, который встречается, и что мне нужно сделать, это прочитать "строку" для каждого строкового значения, но прочитать фиксированное количество байт для байтов и длин.Пока что мое предлагаемое решение состоит в том, чтобы использовать while цикл для чтения байтов во временный байтовый массив до тех пор, пока не появится символ новой строки.Затем преобразуйте байты в строку.Мне это кажется запутанным, но я не вижу другого очевидного способа.Я понимаю, что мог бы использовать StreamReader.ReadLine() но это потребовало бы другого потока, а у меня уже есть NetworkStream.Но если это лучшее решение, я попробую.

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

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

string line = null;  
while (stream.DataAvailable)
{  
    //Get the packet length;  
    UInt16 packetLength = 0;  
    header = new byte[2];  
    stream.Read(header, 0, 2);  
    // Need to reverse the header array for BitConverter class if architecture is little endian.  
    if (BitConverter.IsLittleEndian)
        Array.Reverse(header);  
    packetLength = BitConverter.ToUInt16(header,0);

    buffer = new byte[packetLength];
    stream.Read(buffer, 0, BitConverter.ToUInt16(header, 0));
    line = System.Text.ASCIIEncoding.ASCII.GetString(buffer);
    Console.WriteLine(line);
}
Это было полезно?

Решение

Лично я бы

  1. Поместите Int16 в начале строк , чтобы вы знали, какой длины они будут, и
  2. Используйте IO.Класс BinaryReader, чтобы выполнить чтение, он будет "считывать" целые числа строки, символы и т.д. В переменную, напримерBinReader.ReadInt16() прочитает два байта, вернет значение int16, которое они представляют, и переместит два байта в потоке

Надеюсь, это поможет.

P.S.Будьте осторожны, используя метод ReadString, он предполагает, что строка предваряется пользовательскими 7-битными целыми числами, т.е.что это было написано классом BinaryWriter.Следующее взято из этого КодеГуру Публикация

Класс BinaryWriter имеет два метода для записи строк:перегруженный Метод Write() и WriteString() метод.Первый записывает строку в виде потока байтов в соответствии с кодировкой, используемой классом.Метод WriteString() также использует указанную кодировку, но он добавляет к потоку байтов строки префикс фактической длины строки.Такие строки с префиксами считываются обратно через BinaryReader.ReadString().

Интересная вещь о длине значение его, которое, как несколько байтов, как это возможно используются для хранения таких размеров, это хранится как тип называется 7-бит закодированное число.Если длина соответствует 7 битам, используется один байт, если она больше этого, то устанавливается старший бит на первый байт и создается второй байт путем сдвига значения на 7 бит.Это повторяется с последовательными байтами, пока не наберется достаточно байтов для хранения значения.Этот механизм используется, чтобы убедиться, что длина не становится значительной частью размера, занимаемого сериализованной строкой.BinaryWriter и BinaryReader есть методы чтения и записи 7-бит закодированных чисел, но они защищенный и поэтому их можно использовать только если вам наследовать от этих классов.

Другие советы

Я бы выбрал строки с префиксом длины.Это сделает вашу жизнь намного проще, и это означает, что вы можете представлять строки с разрывами строк внутри.Однако несколько комментариев к вашему коду:

  • Не используйте Stream.Данные доступны.Просто потому, что нет доступных данных сейчас это не значит, что вы прочитали конец потока.
  • Если вы не абсолютно уверены, что вам никогда не понадобится текст, кроме ASCII, не используйте ASCIIEncoding.
  • Не предполагайте, что поток.Read будет считывать все данные, которые вы запрашиваете. Всегда проверьте возвращаемое значение.
  • BinaryReader многое из этого значительно упрощает (включая строки с префиксом длины и чтение, которое повторяется до тех пор, пока не будет прочитано то, о чем вы его просили)
  • Вы вызываете BitConverter.Дважды запускаете параметр 16 для одних и тех же данных.Почему?
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top