二进制 Writer/Reader 额外字符
Binary Writer/Reader extra character
我正在将一些遗留的 VB6 代码转换为 C#,这让我有点困惑。 VB6 代码将某些数据顺序写入文件。此数据始终为 110 字节。我可以在转换后的代码中很好地读取这个文件,但是当我从转换后的代码写入文件时遇到了问题。
这是我在 LINQPad 中快速编写的精简示例:
void Main()
{
int[,] data = new[,]
{
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
},
{
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39
}
};
using ( MemoryStream stream = new MemoryStream() )
{
using ( BinaryWriter writer = new BinaryWriter( stream, Encoding.ASCII, true ) )
{
for( var i = 0; i < 2; i++ )
{
byte[] name = Encoding.ASCII.GetBytes( "Blah" + i.ToString().PadRight( 30, ' ' ) );
writer.Write( name );
for( var x = 0; x < 20; x++ )
{
writer.Write( data[i,x] );
}
}
}
using ( BinaryReader reader = new BinaryReader( stream ) )
{
// Note the extra +4 is because of the problem below.
reader.BaseStream.Seek( 30 + ( 20 * 4 ) + 4, SeekOrigin.Begin );
string name = new string( reader.ReadChars(30) );
Console.WriteLine( name );
// This is the problem..This extra 4 bytes should not be here.
//reader.ReadInt32();
for( var x = 0; x < 20; x++ )
{
Console.WriteLine( reader.ReadInt32() );
}
}
}
}
如你所见,我先写了一个30个字符的字符串。该字符串永远不会超过 30 个字符,如果较短则用空格填充。之后,写入 20 个 32 位整数。它总是 20 个整数。所以我知道字符串中的每个字符都是一个字节。我知道一个 32 位整数是四个字节。所以在我的 reader 示例中,我应该能够查找 110 个字节 ( 30 + (4 * 20) ),读取 30 个字符,然后读取 20 个整数,这就是我的数据。但是,由于某种原因,在字符串后多写了 4 个字节。
我是否只是遗漏了一些非常明显的东西(我自己通常就是这种情况)? .Net 中的字符串不是以 null 结尾的,无论如何这是四个字节,而不仅仅是一个额外的字节?那么这额外的 4 个字节是从哪里来的呢?我没有直接调用 Write(string) 所以它不能是一个前缀长度,这显然不是因为它在我的字符串之后。如果您取消对 ReadInt32() 的注释,它会产生所需的结果。
额外的 4 个字节来自您正在编写的额外 4 个字符。将您编码为 ASCII 的字符串更改为:
("Blah" + i.ToString()).PadRight(30, ' ')
也就是说,在 连接前缀和整数之后 填充字符串。
你额外的四个字节是空格,因为你没有减去 'Blah' 的长度。您不知道自己在流中的什么位置。所以基本上,你认为你只写了 30 个字符,但你实际上写了 34 个字符。
我知道你没有问这个 - 但你正在将垃圾数据写入不需要存在的文件。
不要用空格填充您的字符串,您应该只包含一个 header 或指示文件中下一个字段长度的指针。
例如,假设您有一个 120 字节的文件。该文件的前 4 个字节表示后面的字符串长度为 96 个字节。所以你读取4个字节,得到长度然后读取96个字节。接下来的 4 个字节表示您有一个 16 字节长的字符串,因此您读取接下来的 16 个字节并获取下一个字符串。这几乎是每个定义良好的协议的工作方式。
我正在将一些遗留的 VB6 代码转换为 C#,这让我有点困惑。 VB6 代码将某些数据顺序写入文件。此数据始终为 110 字节。我可以在转换后的代码中很好地读取这个文件,但是当我从转换后的代码写入文件时遇到了问题。
这是我在 LINQPad 中快速编写的精简示例:
void Main()
{
int[,] data = new[,]
{
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19
},
{
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39
}
};
using ( MemoryStream stream = new MemoryStream() )
{
using ( BinaryWriter writer = new BinaryWriter( stream, Encoding.ASCII, true ) )
{
for( var i = 0; i < 2; i++ )
{
byte[] name = Encoding.ASCII.GetBytes( "Blah" + i.ToString().PadRight( 30, ' ' ) );
writer.Write( name );
for( var x = 0; x < 20; x++ )
{
writer.Write( data[i,x] );
}
}
}
using ( BinaryReader reader = new BinaryReader( stream ) )
{
// Note the extra +4 is because of the problem below.
reader.BaseStream.Seek( 30 + ( 20 * 4 ) + 4, SeekOrigin.Begin );
string name = new string( reader.ReadChars(30) );
Console.WriteLine( name );
// This is the problem..This extra 4 bytes should not be here.
//reader.ReadInt32();
for( var x = 0; x < 20; x++ )
{
Console.WriteLine( reader.ReadInt32() );
}
}
}
}
如你所见,我先写了一个30个字符的字符串。该字符串永远不会超过 30 个字符,如果较短则用空格填充。之后,写入 20 个 32 位整数。它总是 20 个整数。所以我知道字符串中的每个字符都是一个字节。我知道一个 32 位整数是四个字节。所以在我的 reader 示例中,我应该能够查找 110 个字节 ( 30 + (4 * 20) ),读取 30 个字符,然后读取 20 个整数,这就是我的数据。但是,由于某种原因,在字符串后多写了 4 个字节。
我是否只是遗漏了一些非常明显的东西(我自己通常就是这种情况)? .Net 中的字符串不是以 null 结尾的,无论如何这是四个字节,而不仅仅是一个额外的字节?那么这额外的 4 个字节是从哪里来的呢?我没有直接调用 Write(string) 所以它不能是一个前缀长度,这显然不是因为它在我的字符串之后。如果您取消对 ReadInt32() 的注释,它会产生所需的结果。
额外的 4 个字节来自您正在编写的额外 4 个字符。将您编码为 ASCII 的字符串更改为:
("Blah" + i.ToString()).PadRight(30, ' ')
也就是说,在 连接前缀和整数之后 填充字符串。
你额外的四个字节是空格,因为你没有减去 'Blah' 的长度。您不知道自己在流中的什么位置。所以基本上,你认为你只写了 30 个字符,但你实际上写了 34 个字符。
我知道你没有问这个 - 但你正在将垃圾数据写入不需要存在的文件。
不要用空格填充您的字符串,您应该只包含一个 header 或指示文件中下一个字段长度的指针。
例如,假设您有一个 120 字节的文件。该文件的前 4 个字节表示后面的字符串长度为 96 个字节。所以你读取4个字节,得到长度然后读取96个字节。接下来的 4 个字节表示您有一个 16 字节长的字符串,因此您读取接下来的 16 个字节并获取下一个字符串。这几乎是每个定义良好的协议的工作方式。