当BinaryReader打开时,使用Stream.Seek是否安全?

11

由于BinaryReader底层的缓冲策略,我不确定在读取存储在流中的偏移量后,重新将流定位到该偏移量以恢复流是否可行。

例如,以下代码是否正确:

using (var reader = new CustomBinaryReader(inputStream))
{
   var offset= reader.ReadInt32();
   reader.BaseStream.Seek(offset, SeekOrigin.Begin);

   //Then resume reading the streaming
}

在查找流之前,我应该关闭第一个二进制读取器,然后重新打开第二个读取器吗?

int offset;
using (var firstReader = new CustomBinaryReader(inputStream))
{
   offset= firstReader.ReadInt32();
}
inputStream.Seek(offset, SeekOrigin.Begin);
using (var secondReader = new CustomBinaryReader(inputStream))
{
   //Then resume reading the streaming
}
3个回答

12

BinaryReader使用缓冲区来读取足够的字节从基础流中转换一个值。换句话说,ReadInt32()会首先缓冲4个字节,ReadDecimal()会首先缓冲16个字节等等。ReadString()方法比较棘手,但它也有应对措施,BinaryWriter将字符串长度首先写入文件进行编码。这样BinaryReader在转换字符串之前就知道要缓冲多少字节。

因此,在ReadXxx()方法之一返回后,缓冲区总是为空,并且在BaseStream上调用Seek()方法也是可以的。这也是Microsoft不需要重写Seek()方法的原因。

MSDN文章中的警告是恰当的,如果您在调用Seek()方法后调用ReadXxx()方法,则肯定会多次读取“offset”值。然而,我认为这是完全有意的。


3
汉斯,为什么在调用 Seek() 方法后再调用 ReadXxx() 方法时需要读取偏移量值多次?难道 BinaryReader 不会重新填充缓冲区并返回正确的值吗?除非偏移量的值等于已写入偏移量本身的位置--否则你将回到起点。 - yagni
1
还有一个原因是微软不需要覆盖Seek()方法。你是否打算在这里写更多内容,还是说已经结束了?这个句子读起来好像你要说更多的话,但最后却戛然而止。 - Water
这是一个值得点击“提问”按钮的问题。 - Hans Passant

4
我认为这并非总是安全的(尽管在某些情况下可能是安全的)。Microsoft关于BinaryReader.BaseStream的文档明确指出:使用底层流进行读取或使用BinaryReader时可能导致数据丢失和破坏。例如,可能会多次读取相同的字节,可能会跳过字节或字符读取可能变得不可预测。因此我建议避免这样做。(有趣的是,BinaryWriter.Seek()方法存在,但没有BinaryReader.Seek()方法。)

3

根据我的经验,只要您在同步使用它们且没有其他线程正在处理流,则它们就可以完美地工作。

我在编写用于处理二进制文件格式的应用程序时广泛使用此方法,并且从未遇到过任何问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接