【问题标题】:C# BinaryWriter Write Method String SizeC# BinaryWriter 写入方法字符串大小
【发布时间】:2017-11-21 09:14:11
【问题描述】:

使用 C# 将字符串写入二进制文件时,长度(以字节为单位)会自动添加到输出之前。根据MSDN documentation,这是一个无符号整数,但也是一个单字节。他们给出的示例是单个 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) 方法,但出于显而易见的原因,我想避免这种情况(类似的问题 herehere 接受这个作为答案)。另一个更容易接受的解决方法是让读者寻找一个特定的字符来开始 ASCII 字符串信息(可能是一个不可打印的字符?),但这并不是万无一失的。最后一种解决方法(我能想到的)是强制字符串在特定数量的大小字节的大小范围内;再说一遍,这并不理想。

虽然强制字节数组的大小保持一致是最简单的,但我可以控制阅读器,因此也欢迎任何聪明的阅读器解决方案。

【问题讨论】:

  • 它使用variable-length 7-bit encoding。一个微优化,很少有理由为此生气。如果您不喜欢它,请考虑 Encoding.UTF8.GetBytes() 但不要忘记序列化 byte[] 数组的长度,以便您可以正确读取它。不要用7位编码,呵呵。
  • 您确定长度在 128 到 255 之间的字符串实际上将长度存储为单个字节吗?
  • @MatthewWatson 我确定他们不是 :)
  • @AndyK。在该编码中,每个字节都有关于是否有另一个字节的信息(这就是它是 7 位编码的原因——最后一位用于那个)。因此,您读取 1 个字节,然后检查该位并决定是否需要读取下一个字节。这意味着您始终可以读取字符串长度,即使该长度是在可变长度数组中编码的。
  • @AndyK。这是Read7BitEncodedInt的参考来源:referencesource.microsoft.com/#mscorlib/system/io/…

标签: c# string size binaryfiles


【解决方案1】:

BinaryWriterBinaryReader 并不是写入二进制数据的唯一方式;简单地说:它们提供一个约定,在那个特定的阅读器和作者之间共享。不,您不能告诉他们使用其他约定——当然,除非您将它们两个子类化并完全覆盖 ReadStringWrite(string) 方法。

如果您想使用不同的约定,那么只需:不要使用BinaryReaderBinaryWriter。直接使用任何文本EncodingStream 交谈非常容易,您想要获取字节和字节数。然后你可以使用你想要的任何约定。如果您只需要写入高达 65k 的字符串,那么请确保:使用固定的 2 个字节(无符号短)。当然,您还需要决定先出现哪个字节(“字节序”)。

至于前缀的大小:本质上是使用:

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);
}

这种类型的长度编码非常常见 - 例如,它与 "varint" that "protobuf" uses 的想法相同(base-128,最低有效组在前,在 7 位组中保留位顺序,第 8 位作为延续)

【讨论】:

  • 将它们称为约定很有意义,因此为什么不能在不覆盖方法的情况下更改它们以满足您的需求。您上面的评论强化了这一点,并且是我对它们的看法的范式转变。
  • @AndyK。老实说,听起来你应该直接与Stream打交道……
  • 我正在将人类可读的标头信息写入数据文件,使用非常简单的 write(string) 方法非常诱人,从表面上看,它可以满足我的所有需求。我认为你是对的。
【解决方案2】:

如果你想自己写长度:

using (var bw = new BinaryWriter(fs))
{
  bw.Write(length); // Use a byte, a short...
  bw.Write(Encoding.Unicode.GetBytes("Your string"));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-25
    • 2012-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多