Читать строку из байтового массива (не конвертировать байтовый массив в строку) - PullRequest
6 голосов
/ 29 января 2009

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

// 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, чтобы я мог прочитать длину и затем прочитать String на основе указанной длины.

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

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

Ответы [ 2 ]

9 голосов
/ 29 января 2009

Лично я бы

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

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

P.S. Будьте осторожны, используя метод ReadString, он предполагает, что к строке добавлены пользовательские 7-битные целые числа, т. Е. Что она была записана классом BinaryWriter. Вот из этого CodeGuru post

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

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

5 голосов
/ 29 января 2009

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

  • Не использовать Stream.DataAvailable. То, что сейчас нет доступных данных , не означает, что вы прочитали конец потока.
  • Если вы абсолютно не уверены, что вам никогда не понадобится текст, кроме ASCII, не используйте ASCIIEncoding.
  • Не думайте, что Stream.Read будет читать все данные, которые вы запрашиваете. Всегда проверить возвращаемое значение.
  • BinaryReader делает многое из этого намного проще (включая строки с префиксом длины и циклическое чтение, пока оно не прочитает то, что вы просили)
  • Вы дважды вызываете BitConverter.ToUInt16 для одних и тех же данных. Почему?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...