【问题标题】:BinaryReader ReadString specifying length?BinaryReader ReadString 指定长度?
【发布时间】:2013-11-11 16:55:17
【问题描述】:

我正在开发一个解析器来接收 UDP 信息、解析它并存储它。为此,我使用BinaryReader,因为它主要是二进制信息。不过,其中一些将是字符串。 MSDN says 用于 ReadString() 函数:

从当前流中读取一个字符串。字符串前缀为 长度,编码为整数,一次七位。

我完全理解它,直到“一次七位”我试图简单地忽略它,直到我开始测试。在将其放入MemoryStream 并尝试使用BinaryReader 读取它之前,我正在创建自己的字节数组。这是我最初认为可行的方法:

byte[] data = new byte[] { 3, 0, 0, 0, (byte)'C', (byte)'a', (byte)'t', }
BinaryReader reader = new BinaryReader(new MemoryStream(data));
String str = reader.ReadString();

知道int 是4 个字节(并且玩弄了足够长的时间以发现BinaryReader 是Little Endian)我将其传递给3 的长度和相应的字母。然而str 最终持有\0\0\0。如果我删除了 3 个零并且只有

byte[] data = new byte[] { 3, (byte)'C', (byte)'a', (byte)'t', }

然后它会正确读取和存储Cat。对我来说,这与文档说长度应该是整数相冲突。现在我开始认为它们只是表示没有小数位的数字,而不是数据类型int。这是否意味着BinaryReader 永远无法读取大于 127 个字符的字符串(因为这将是 01111111 对应于文档的 7 位部分)?

我正在编写一个协议,在将文档传递给我们的客户之前,我需要完全了解我所涉及的内容。

【问题讨论】:

  • BinaryReader 旨在读取使用 BinaryWriter 写出的内容。所以尝试用 BinaryWriter 写出不同长度的字符串,你应该能够弄清楚协议。
  • 但是你最好弄清楚那个 UDP 协议是如何向你发送数据的,当它没有为字符串添加前缀时(这是最有可能的),这一切都是徒劳的。
  • 我正在为我的工作定义协议,发送数据的代码很可能不会用 C#(可能是 Linux 上的 python 或 C)编写,因此无法访问 BinaryWriter。我正在使用 BinaryReader 来提高代码的可读性,尽管我可能会放弃 ReadString 并使用固定的 4 字节作为长度并使用 ReadChars 以便更容易实现。
  • 我的编码错了,它使用的是“Writers current encoding”。

标签: c# binaryreader


【解决方案1】:

除非他们特别说“int”或“Int32”,否则他们只是表示整数中的整数。

“7 位一次”是指它实现了 7 位长度编码,起初看起来有点混乱,但实际上相当简单。以下是一些示例值以及它们是如何使用 7 位长度编码写出的:

/*
decimal value   binary value                ->  enc byte 1   enc byte 2   enc byte 3
85              00000000 00000000 01010101  ->  01010101     n/a          n/a
1,365           00000000 00000101 01010101  ->  11010101     00001010     n/a
349,525         00000101 01010101 01010101  ->  11010101     10101010     00010101
*/

上面的表格使用大端序,只是我不得不选择一个,这是我最熟悉的。 7 位长度编码的工作方式,本质上是小端序。

请注意,85 次写入 1 字节,1,365 次写入 2 字节,349,525 次写入 3 字节。

这是同一张表,使用字母来显示每个值的位在写入输出中的使用方式(破折号是零值位,0 和 1 是编码机制添加的,以指示后续字节是否是写/读)...

/*
decimal value   binary value                ->  enc byte 1   enc byte 2   enc byte 3
85              -------- -------- -AAAAAAA  ->  0AAAAAAA     n/a          n/a
1,365           -------- -----BBB AAAAAAAA  ->  1AAAAAAA     0---BBBA     n/a
349,525         -----CCC BBBBBBBB AAAAAAAA  ->  1AAAAAAA     1BBBBBBA     0--CCCBB
*/

所以 0 到 2^7-1 (127) 范围内的值将写入 1 个字节,2^7 (128) 到 2^14-1 (16,383) 的值将使用 2 个字节,2^ 14 (16,384) 到 2^21-1 (2,097,151) 将占用 3 个字节,以此类推。

【讨论】:

    【解决方案2】:

    我为BinaryReader 找到了source code。它使用了一个名为 Read7BitEncodedInt() 的函数,在查找该文档和 Write7BitEncodedInt() 的文档后,我发现了这个:

    value 参数的整数以 7 位的形式写入 时间,从七个最低有效位开始。高位 一个字节表示在此之后是否还有更多字节要写入 一。如果值适合七位,则只占用一个字节的空间。 如果值不适合七位,则在第一个设置高位 字节并写出。然后将值移动七位,然后下一个 字节被写入。重复这个过程,直到整个整数 已经写好了。

    此外,Ralf 发现 this link 可以更好地显示正在发生的事情。

    【讨论】:

    • 但是如果二进制流是由运行在 Big Endian 机器上的程序写入然后由 Little Endian 机器上的另一个程序读取(例如,如果文件被传输)呢?我认为他们错过了这一点。我正在编写一个应用程序,我在其中以 Big Endian(网络字节顺序)编写流。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-30
    • 1970-01-01
    • 2013-12-12
    • 1970-01-01
    • 2019-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多