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;
let cursor = 0;
let nextByte: number;
do {
nextByte = buffer[offset + cursor];
length = length | ((nextByte & 0x7F) << (cursor * 7));
cursor++;
}
while (nextByte >= 0x80);
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") {
let view = new DataView(buffer.buffer, offset, 4);
let length = view.getInt32(0);
let sliceWithString = buffer.slice(offset + 4, offset + 4 + length);
let decoder = new TextDecoder(encoding);
return decoder.decode(sliceWithString);
}
}