BinaryReader ReadString с указанием длины?

Я работаю над синтаксическим анализатором для получения информации UDP, ее анализа и сохранения. Для этого я использую BinaryReader, поскольку в основном это будет двоичная информация. Хотя некоторые из них будут строками. MSDN говорит о функции ReadString():

Читает строку из текущего потока. Строка имеет префикс длины, закодированный как целое число семь бит за раз.

И я полностью это понимаю вплоть до «семи битов за раз», которые я пытался просто игнорировать, пока не начал тестировать. Я создаю свой собственный массив байтов, прежде чем помещать его в MemoryStream и пытаться прочитать его с помощью BinaryReader. Вот то, что я сначала подумал, будет работать:

byte[] data = new byte[] { 3, 0, 0, 0, (byte)'C', (byte)'a', (byte)'t', }
BinaryReader reader = new BinaryReader(new MemoryStream(data));
String str = reader.ReadString();

Зная, что int составляет 4 байта (и поигравшись достаточно долго, чтобы выяснить, что BinaryReader — это Little Endian), я передаю ему длину 3 и соответствующие буквы. Однако str в конечном итоге держит \0\0\0. Если я уберу 3 нуля и просто

byte[] data = new byte[] { 3, (byte)'C', (byte)'a', (byte)'t', }

Затем он правильно читает и сохраняет Cat. Для меня это противоречит документации, в которой говорится, что длина должна быть целым числом. Теперь я начинаю думать, что они просто означают число без десятичной точки, а не тип данных int. Означает ли это, что BinaryReader никогда не сможет прочитать строку, длиннее 127 символов (поскольку это будет 01111111, что соответствует 7-битной части документации)?

Я пишу протокол, и мне нужно полностью понять, во что я ввязываюсь, прежде чем передать нашу документацию нашим клиентам.


person Corey Ogburn    schedule 31.10.2013    source источник
comment
BinaryReader предназначен для чтения данных, записанных с помощью BinaryWriter. Поэтому попробуйте написать строки разной длины с помощью BinaryWriter, и вы сможете понять протокол.   -  person President James K. Polk    schedule 31.10.2013
comment
Но вам лучше выяснить, как этот UDP-протокол отправляет вам данные, когда он не префикс строки (и это наиболее вероятно), это все напрасно.   -  person Henk Holterman    schedule 31.10.2013
comment
comment
Я определяю протокол для своей работы, и код, который отправляет данные, скорее всего, не будет написан на C# (вероятно, на python или C в Linux) и, следовательно, не будет иметь доступа к BinaryWriter. Я использую BinaryReader для удобочитаемости кода, хотя я могу отказаться от ReadString и использовать твердые 4 байта для длины и использовать ReadChars, чтобы его было проще реализовать.   -  person Corey Ogburn    schedule 31.10.2013
comment
Я ошибся насчет кодировки, она использует текущую кодировку Writers.   -  person Henk Holterman    schedule 31.10.2013


Ответы (2)


Я нашел исходный код для BinaryReader. Он использует функцию под названием Read7BitEncodedInt() и просмотрев эту документацию и документацию для Write7BitEncodedInt() Я нашел это:

Целое число параметра value записывается по семь бит за раз, начиная с семи младших битов. Старший бит байта указывает, есть ли еще байты, которые нужно записать после этого. Если значение умещается в семи битах, оно занимает всего один байт. Если значение не умещается в семи битах, старший бит устанавливается в первый байт и записывается. Затем значение сдвигается на семь бит и записывается следующий байт. Этот процесс повторяется до тех пор, пока не будет записано все целое число.

Кроме того, Ральф нашел эту ссылку, лучше отображает происходящее.

person Corey Ogburn    schedule 31.10.2013
comment
Но что, если двоичный поток был записан программой, работающей на машине с прямым порядком байтов, а затем прочитан (например, если файл транспортируется) другой программой на машине с прямым порядком байтов? Я думаю, что они пропустили это. Я пишу приложение, в котором я пишу потоки в Big Endian (сетевой порядок байтов). - person Lord of Scripts; 02.04.2016

Если они специально не говорят «int» или «Int32», они просто означают целое число как целое число.

Под «7 битами за раз» они подразумевают, что он реализует 7-битное кодирование длины, что на первый взгляд кажется немного запутанным, но на самом деле довольно просто. Вот несколько примеров значений и то, как они записываются с использованием 7-битной кодировки:

/*
decimal value   binary value                ->  enc byte 1   enc byte 2   enc byte 3
85              00000000 00000000 01010101  ->  01010101     n/a          n/a
1,365           00000000 00000101 01010101  ->  11010101     00001010     n/a
349,525         00000101 01010101 01010101  ->  11010101     10101010     00010101
*/

В приведенной выше таблице используется обратный порядок байтов только по той причине, что я просто должен был выбрать один, и это то, с чем я больше всего знаком. То, как работает 7-битное кодирование длины, по своей природе является прямым порядком байтов.

Обратите внимание, что 85 записывается в 1 байт, 1365 записывается в 2 байта, а 349 525 записывается в 3 байта.

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

/*
decimal value   binary value                ->  enc byte 1   enc byte 2   enc byte 3
85              -------- -------- -AAAAAAA  ->  0AAAAAAA     n/a          n/a
1,365           -------- -----BBB AAAAAAAA  ->  1AAAAAAA     0---BBBA     n/a
349,525         -----CCC BBBBBBBB AAAAAAAA  ->  1AAAAAAA     1BBBBBBA     0--CCCBB
*/

Таким образом, значения в диапазоне от 0 до 2^7-1 (127) будут записаны как 1 байт, значения от 2^7 (128) до 2^14-1 (16 383) будут использовать 2 байта, 2^14 (16 384 ) до 2^21-1 (2 097 151) займет 3 байта и т. д. и т. д.

person dynamichael    schedule 26.05.2020