C#文件流定位 C#如何使用Seek在文件任意位置读写

来源:这里教程网 时间:2026-02-21 17:41:43 作者:

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
本身,而是你假设“文件是个字节数组”却忘了编码、缓冲、硬件寻道这些现实约束。

相关推荐