使用C#将字符串写入二进制文件时,长度(以字节为单位)会自动添加到输出中。根据MSDN文档,这是一个无符号整数,但也是一个单字节。他们给出的例子是,一个UTF-8字符将是三个写入的字节:1个大小的字节和2个字符字节。这对于长度为255的字符串来说是很好的,并且与我观察到的行为相匹配。
但是,如果字符串长度超过255字节,则无符号整数的大小会根据需要增加。作为一个简单的例子,将1024个字符视为:
string header = "ABCDEFGHIJKLMNOP";
for (int ii = 0; ii < 63; ii++)
{
header += "ABCDEFGHIJKLMNOP";
}
fileObject.Write(header);
结果是在字符串前面有2个字节。创建一个2^17长度的字符串会导致一个有点令人抓狂的3字节数组。
因此,问题是如何知道在读取时要读取多少字节才能获得后续内容的大小?我不一定事先知道报头大小。最终,我可以强制Write(string)方法始终使用一致的大小(比如2个字节)吗
一个可能的解决方法是编写我自己的write(string)方法,但出于明显的原因,我希望避免这种情况(这里和这里的类似问题都接受这个答案)。另一个更可取的解决方法是让读者寻找一个特定的字符来启动ASCII字符串信息(可能是一个不可打印的字符?),但这并不是绝对正确的。最后一个解决方法(我能想到)是强制字符串在特定大小字节数的大小范围内;再说一遍,这是不理想的。
虽然强制字节数组的大小保持一致是最简单的,但我可以控制读取器,所以任何聪明的读取器解决方案都是受欢迎的。
BinaryWriter
和BinaryReader
不是写入二进制数据的唯一方法;简单地说:它们为提供了一个约定,该约定在特定的读取器和写入器之间共享。不,你不能告诉他们使用另一个约定——当然,除非你同时对它们进行子类化,并完全覆盖ReadString
和Write(string)
方法。
如果您想使用不同的约定,请简单地:不要使用BinaryReader
和BinaryWriter
。直接使用想要获取字节和字节计数的任何文本Encoding
与Stream
对话是非常容易的。然后,您可以使用任何您想要的约定。如果您只需要写入高达65k的字符串,那么请确保:使用固定的2字节(无符号短)。当然,您还需要决定哪个字节先出现("endianness")。
至于前缀的大小:它本质上是使用:
int byteCount = this._encoding.GetByteCount(value);
this.Write7BitEncodedInt(byteCount);
带有:
protected void Write7BitEncodedInt(int value)
{
uint num = (uint) value;
while (num >= 0x80)
{
this.Write((byte) (num | 0x80));
num = num >> 7;
}
this.Write((byte) num);
}
这种类型的长度编码非常常见-例如,它与"protobuf"使用的"variant"思想相同(以-128为基数,最低有效组优先,在7位组中保留位顺序,第8位作为延续)
如果你想自己写长度:
using (var bw = new BinaryWriter(fs))
{
bw.Write(length); // Use a byte, a short...
bw.Write(Encoding.Unicode.GetBytes("Your string"));
}