Seek 方法必须配合可读写流使用
直接对
FileStream调用
Seek失败,大概率是因为流没以支持定位的模式打开。.NET 中只有打开时指定
FileAccess.ReadWrite或对应权限,且底层文件句柄支持随机访问(如磁盘文件),
CanSeek才为
true。只用
FileAccess.Read打开的流,即使物理上可定位,
CanSeek也可能返回
false—— 这是 .NET 的安全限制,不是 bug。
实操建议:
用new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None, 4096, FileOptions.None)显式声明读写权限 调用前务必检查
stream.CanSeek,避免运行时报
NotSupportedException网络流、加密流(如
CryptoStream)、压缩流(如
GZipStream)通常不支持
Seek,别强行调用
SeekOrigin 的三个取值含义和常见误用
SeekOrigin.Begin、
SeekOrigin.Current、
SeekOrigin.End看似简单,但容易在边界计算出错。尤其是
SeekOrigin.End:它把偏移量当作“从末尾往前数”,所以要定位到倒数第 10 字节,得传
-10,而不是
10;若传正数会抛
IOException(“试图访问无效位置”)。
实操建议:
用stream.Length获取总长度,再结合
SeekOrigin.Begin计算更直观,比如跳到第 1024 字节:
stream.Seek(1024, SeekOrigin.Begin)修改当前位置后立即读写,别依赖“上次位置”,
Seek不会自动刷新缓冲区,后续
Read/
Write才真正触发 I/O 向文件末尾追加数据时,不要用
SeekOrigin.End再写,直接用
FileMode.Append更安全
Seek 后读写中文或 UTF-8 字符串容易乱码
这是最隐蔽的问题:
Seek操作的是字节偏移,而字符串编码(如 UTF-8)中一个汉字占 2~4 字节。如果用
StreamReader读文本,它内部有解码缓冲区,
Seek直接跳到中间字节会导致后续
ReadLine解码失败,抛
ArgumentException(“数据无效”)。
实操建议:
纯文本场景下,避免混合使用Seek和
StreamReader/
StreamWriter;改用
FileStream+
Encoding.GetBytes/
GetString手动处理字节 若必须按行操作,先用
stream.Position记录每行起始字节位置,
Seek时只跳到这些已知合法位置 UTF-8 BOM(
EF BB BF)占 3 字节,开头
Seek(0, ...)是安全的,但
Seek(1, ...)就可能卡在 BOM 中间
大文件 Seek 性能与内存映射替代方案
对 GB 级文件反复
Seek+ 小块读写,性能可能比顺序读还差——因为每次
Seek触发磁盘寻道,机械硬盘尤其明显。而且
FileStream默认缓冲区 4KB,小偏移频繁切换位置会让缓存失效。
实操建议:
优先用MemoryMappedFile处理超大文件随机访问,它把文件映射到虚拟内存,
Seek变成指针运算,无 I/O 开销 若坚持用
FileStream,增大缓冲区(如 64KB),并尽量批量读写,减少
Seek次数 注意:32 位进程下
Position是
long,但某些旧系统 API 对 >2GB 文件支持不稳,测试时务必覆盖边界值
真正麻烦的从来不是
Seek本身,而是你假设“文件是个字节数组”却忘了编码、缓冲、硬件寻道这些现实约束。
