JavaScript中与C#中BinaryReader.ReadString()方法等效的方法是什么?

4
我正在将一些C#代码转换为JavaScript代码,尽管该文件具有多种数据类型,但我在跨库的Javascrip中找到了匹配功能,但是我无法在JS中找到一个特定的函数。

那个函数是https://learn.microsoft.com/en-us/dotnet/api/system.io.binaryreader.readstring?view=net-7.0

我有几个问题:

  1. 首先让我困惑的是,字符串本质上不是一个可变长度的变量吗?如果是这样,为什么这个函数不需要一个长度参数?
  2. 假设字符串有一些长度限制。如果是这样,JS/TS是否具有类似的功能?或者有任何包可以下载来模仿C#的功能?

提前致谢。


1
它看起来只是一个可读流和DataView的组合?这是在浏览器还是Node.js中? - zenly
1
从当前流中读取一个字符串。该字符串以长度为前缀,每次编码为七位整数。 - Oliver Weichhold
1
从链接中:“从当前流中读取字符串。该字符串以长度为前缀,编码为每次七位的整数”。 - Poul Bak
@caTS 是一个浏览器。 - Lost
@caTS,我正在查看数据视图,但它似乎没有任何关于字符串的内容。 - Lost
显示剩余3条评论
1个回答

2
BinaryReader希望字符串按照特定格式进行编码,这是BinaryWriter所写的格式。根据文档所述:
从当前流中读取一个字符串。该字符串前缀为长度,以每次7位编码为一个整数。
因此,字符串的长度存储在字符串本身之前,以“每次7位编码为一个整数”的方式进行编码。我们可以从BinaryWriter.Write7BitEncodedInt获取有关此的更多信息。
参数值的整数将以每次7位的方式写出,从最低有效位的7位开始。字节的高位指示是否在此之后还有更多字节要写入。如果值适合7位,则只需要一个字节的空间。如果值不适合7位,则在第一个字节上设置高位并写出。然后将值向左移动7位,并写入下一个字节。重复此过程,直到整个整数被写入。
因此,这是可变长度编码:与始终使用4个字节的Int32值的通常方法不同,此方法使用可变数量的字节。这样,短字符串的长度可以占用少于4个字节(例如,长度小于128个字节的字符串仅需要1个字节)。
您可以在JavaScript中复制此逻辑-只需一次读取一个字节。最低的7位表示长度信息(部分),而最高位指示下一个字节是否也表示长度信息(否则它是实际字符串的开头)。
然后,当您获得长度时,请使用TextDecoder将字节数组解码为给定编码的字符串。这是相同的TypeScript函数。它接受缓冲区(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);
  }
}

如果以上代码将用于生产环境,建议添加各种合理性检查(例如,我们不要读取太多字节以获取长度,计算出的长度实际上在缓冲区范围内等)。

下面是使用C#中BinaryWriter.Write(string)方法写入字符串"TEST STRING"的二进制表示的小测试:

let buffer = new Uint8Array([12, 84, 69, 83, 84, 32, 83, 84, 82, 73, 78, 71, 33]);
let reader = new BinaryReader();
console.log(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。只有LSB包含任何值。因此回到您的代码,while (nextByte >= 0x80)不会在它读取的第一个字节中退出吗?因为前三个字节实际上都是0。对于长度为29的字符串,4个字节(转换为UInt8Array)看起来像[0,0,0,29],我认为这是所有问题的根源。就像您一样,我期望每个字节的MSB为1。 - Lost
这意味着字节数组不是通过BinaryWriter.WriteString创建的,因此不能使用BinaryReader.ReadString读取。因此,在这种情况下,您不需要寻找BinaryReader的类似物,正如您的问题所提到的那样。但我已经更新了答案,并提供了可能的解决方案。 - Evk
1
谢谢,您的原始答案有效。我读取字节的方式有误。 - Lost

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接