Эквивалент Javascript для BinaryReader.ReadString() из С#

Я конвертирую некоторый код C# в код JavaScript, и хотя этот файл имеет несколько типов данных, и я нашел соответствующие функции в Javascript во всех библиотеках, я не могу найти одну конкретную функцию в JS.

Эта функция https://learn.microsoft.com/en-us/dotnet/api/system.io.binaryreader.readstring?view=net-7.0

Есть пара вопросов, которые у меня есть:

  1. Прежде всего, что меня смущает, так это то, что строка по своей сути не является переменной переменной длины? Если да, то как эта функция может не принимать аргумент длины?
  2. Предположим, что существует некоторый предел длины строки. Если да, есть ли у JS/TS аналогичная функциональность? Или любой пакет, который я могу загрузить, чтобы имитировать функциональность С#?

Заранее спасибо.

Это просто похоже на читаемый поток и, возможно, DataView вместе? Это в браузере или Node.js?

vera. 20.10.2022 18:29

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

Oliver Weichhold 20.10.2022 18:32

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

Poul Bak 20.10.2022 18:33

@caTS это браузер.

Lost 20.10.2022 18:36

Глядя на ответы @OliverWeichhold и poul, я понимаю, что переменная длина не является проблемой, поскольку JS может выполнять эту работу. Теперь вопрос в том, может ли JS это сделать?

Lost 20.10.2022 18:37

@caTS, я смотрю на представление данных, но похоже, что в нем нет ничего для строк.

Lost 20.10.2022 18:40

Каков ваш вклад в javascript? Что-то вроде Uint8Array?

Evk 23.10.2022 13:36

@Евк, да. По сути, это двоичный файл, написанный на C#. При чтении Javascript он читает его как Uint8array. Но строки в C# особенные. Первые 4 байта зарезервированы для длины.

Lost 23.10.2022 15:20
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
8
125
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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 в каждом байте.

Lost 23.10.2022 18:54

Это просто означает, что массив байтов не был создан с помощью BinaryWriter.WriteString и поэтому не может быть прочитан с помощью BinaryReader.ReadString. Так что в этом случае вы не ищете аналог BinaryReader, как упоминается в вашем вопросе. Однако я обновил ответ, указав возможное решение для этого случая.

Evk 23.10.2022 19:20

Спасибо, ваш оригинальный ответ сработал. У меня была ошибка в том, как я читал байты.

Lost 24.10.2022 07:14

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