Я конвертирую некоторый код C# в код JavaScript, и хотя этот файл имеет несколько типов данных, и я нашел соответствующие функции в Javascript во всех библиотеках, я не могу найти одну конкретную функцию в JS.
Эта функция https://learn.microsoft.com/en-us/dotnet/api/system.io.binaryreader.readstring?view=net-7.0
Есть пара вопросов, которые у меня есть:
Заранее спасибо.
«Читает строку из текущего потока. Строка имеет префикс длины, закодированный как целое число, состоящее из семи битов за раз».
Из ссылки: «Читает строку из текущего потока. Строка имеет префикс длины, закодированный как целое число семь бит за раз».
@caTS это браузер.
Глядя на ответы @OliverWeichhold и poul, я понимаю, что переменная длина не является проблемой, поскольку JS может выполнять эту работу. Теперь вопрос в том, может ли JS это сделать?
@caTS, я смотрю на представление данных, но похоже, что в нем нет ничего для строк.
Каков ваш вклад в javascript? Что-то вроде Uint8Array?
@Евк, да. По сути, это двоичный файл, написанный на C#. При чтении Javascript он читает его как Uint8array. Но строки в C# особенные. Первые 4 байта зарезервированы для длины.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


BinaryReader ожидает, что строки будут закодированы в определенном формате — формат BinaryWriter их записывает. Как указано в документации:
Читает строку из текущего потока. Строка имеет префикс длина, закодированная как целое число семь бит за раз
Таким образом, длина строки сохраняется прямо перед самой строкой, закодированной «как целое число семь бит за раз». Мы можем получить больше информации об этом из BinaryWriter.Write7BitEncodedInt:
Целое число параметра значения записывается семью битами в время, начиная с семи младших битов. старший бит байт указывает, есть ли еще байты, которые нужно записать после этого один.
Если значение умещается в семи битах, оно занимает всего один байт. Если значение не умещается в семи битах, старший бит устанавливается на первый байт и записывается. значение затем сдвигается на семь битов и следующее байт пишется. Этот процесс повторяется до тех пор, пока все целое число не будет было написано.
Итак, это кодирование с переменной длиной: в отличие от обычного подхода, всегда использующего 4 байта для значения Int32, этот подход использует переменное количество байтов. Таким образом, длина короткой строки может занимать менее 4 байтов (например, строки длиной менее 128 байтов будут занимать всего 1 байт).
Вы можете воспроизвести эту логику в javascript — просто читайте по одному байту за раз. Младшие 7 бит представляют (часть) информацию о длине, а старший бит указывает, представляет ли следующий байт также информацию о длине (в противном случае это начало фактической строки).
Затем, когда вы получили длину - используйте TextDecoder для декодирования массива байтов в строку заданной кодировки. Вот та же функция в машинописи. Он принимает буфер (Uint8Array), смещение в этот буфер и кодировку (по умолчанию UTF-8, проверьте документы TextDecoder для других доступных кодировок):
class BinaryReader {
getString(buffer: Uint8Array, offset: number, encoding: string = "utf-8") {
let length = 0; // length of following string
let cursor = 0;
let nextByte: number;
do {
// just grab next byte
nextByte = buffer[offset + cursor];
// grab 7 bits of current byte, then shift them according to this byte position
// that is if that's first byte - do not shift, second byte - shift by 7, etc
// then merge into length with or.
length = length | ((nextByte & 0x7F) << (cursor * 7));
cursor++;
}
while (nextByte >= 0x80); // do this while most significant bit is 1
// get a slice of the length we got
let sliceWithString = buffer.slice(offset + cursor, offset + cursor + length);
let decoder = new TextDecoder(encoding);
return decoder.decode(sliceWithString);
}
}
Стоит добавить различные проверки работоспособности в приведенный выше код, если он будет использоваться в производстве (что мы не читаем слишком много байтов, читая длину, что вычисленная длина фактически находится в пределах буфера и т. д.).
Небольшой тест, использующий двоичное представление строки "TEST STRING", написанной BinaryWriter.Write(string) на C#:
let buffer = new Uint8Array([12, 84, 69, 83, 84, 32, 83, 84, 82, 73, 78, 71, 33]);
let reader = new BinaryReader();
console.info(reader.getString(buffer, 0, "utf-8"));
// outputs TEST STRING
Обновлять. Вы упоминаете в комментариях, что в ваших данных длина строки представлена 4 байтами, поэтому, например, длина 29 представлена [0, 0, 0, 29]. Это означает, что ваши данные не были записаны с использованием BinaryWriter и поэтому не могут быть прочитаны с помощью BinaryReader, поэтому вам фактически не нужен аналог BinaryReader.GetString, вопреки тому, что задает ваш вопрос.
В любом случае, если вам нужно обработать такой случай - вы можете это сделать:
class BinaryReader {
getString(buffer: Uint8Array, offset: number, encoding: string = "utf-8") {
// create a view over first 4 bytes starting at offset
let view = new DataView(buffer.buffer, offset, 4);
// read those 4 bytes as int 32 (big endian, since your example is like that)
let length = view.getInt32(0);
// get a slice of the length we got
let sliceWithString = buffer.slice(offset + 4, offset + 4 + length);
let decoder = new TextDecoder(encoding);
return decoder.decode(sliceWithString);
}
}
Большое спасибо, это потрясающе. Я попробовал ваш код. для строки длиной 29. Но затем, когда я читаю первые три байта, они буквально становятся равными 0. Только младший бит содержит какое-либо значение. Итак, возвращаясь к вашему коду, не выйдет ли while (nextByte >= 0x80) в первом прочитанном байте? Потому что первые три байта буквально равны 0. Для строки длиной 29 4 байта (преобразованные в UInt8Array) выглядят как [0,0,0,29]. Я думаю, что это корень всей проблемы. Я, как и вы, ожидаю, что MSB будет равен 1 в каждом байте.
Это просто означает, что массив байтов не был создан с помощью BinaryWriter.WriteString и поэтому не может быть прочитан с помощью BinaryReader.ReadString. Так что в этом случае вы не ищете аналог BinaryReader, как упоминается в вашем вопросе. Однако я обновил ответ, указав возможное решение для этого случая.
Спасибо, ваш оригинальный ответ сработал. У меня была ошибка в том, как я читал байты.
Это просто похоже на читаемый поток и, возможно, DataView вместе? Это в браузере или Node.js?