C++ seekg 与 seekp 用法:文件指针移动、二进制模式与状态位
C++ 里 seekg(seek get)用于移动输入文件流的读取位置,与写流的 seekp 成对出现。看似简单,实际有文本模式与二进制模式的行为差异、游标基准选择、失败状态位三个容易出错的地方。本文按“原型 → 三种基准 → 与 tellg 配合 → 常见坑”的顺序说明。

一、函数原型与语义
basic_istream& seekg (streampos pos);
basic_istream& seekg (streamoff off, ios_base::seekdir way);
istream&指输入流(ifstream、istringstream)。- 单参数版本:绝对定位到
pos(通常来自tellg()的返回值)。 - 双参数版本:
off是相对偏移量,way是基准。
seekdir 的三个取值:ios_base::beg(开头)、ios_base::cur(当前位置)、ios_base::end(末尾)。
二、四种常用写法
#include <fstream>
#include <iostream>
std::ifstream fin("data.bin", std::ios::binary); // 关键:用二进制模式
// 1) 跳到文件末尾前 10 字节
fin.seekg(-10, std::ios::end);
// 2) 回到文件开头重读
fin.seekg(0, std::ios::beg);
// 3) 记录位置,稍后回跳
std::streampos p = fin.tellg();
fin.seekg(100, std::ios::cur); // 从当前再往后 100 字节
fin.seekg(p); // 回到记录点
// 4) 读第 N 条定长记录(结构体数组)
struct Rec { int id; char name[28]; }; // 假设 sizeof(Rec)==32
std::size_t n = 5;
fin.seekg(static_cast<std::streamoff>(n * sizeof(Rec)), std::ios::beg);
Rec r; fin.read(reinterpret_cast<char*>(&r), sizeof r);
三、文本模式的陷阱(最重要的差异)
标准规定:在文本流上,seekg 的偏移量必须是 tellg() 返回过的值,或 0(配合 beg)。因为文本模式下换行可能被转换(Windows 的 \r\n 读入时变 \n),逻辑字节数与物理字节数不一致,随手写 seekg(100, beg) 的行为是未定义的。
因此规则很简单:需要精确字节跳转就打开 ios::binary;纯文本按行处理就别用 seek,改用循环 getline。
补充:对文本流做“跳到最后一行”这类需求,可靠做法是把 tellg() 保存下来的位置存进数组,之后 seekg(保存值) 回跳,而不是猜偏移。
四、失败时怎么判断
fin.clear(); // 先清掉可能的 fail/eof 位
fin.seekg(pos, std::ios::beg);
if (fin.fail()) {
std::cerr << "seek failed\n";
}
关键点:读过 eof 或读到失败后再 seek,必须先 clear(),否则状态位残留会让后续所有读操作直接失败——这是 seekg“看起来无效”最常见的真实原因。
另外:seekg 超出文件末尾在二进制流上并不报错(后续读会立刻触发 eof),所以“定位是否合法”要靠随后的 read/tellg 校验,而不是 seek 的返回值。
五、与 seekp、与随机读写的配合
fstream以ios::in | ios::out | ios::binary打开时,读写游标在多数实现里共用一个位置,但标准并未保证;混用 seekg/seekp 时应显式设置,并在切换读写方向时调用clear()(in/out 之间切换需要seek或clear才能安全)。istringstream也支持seekg,常用于在字符串里做分段解析。
std::fstream io("file.bin", std::ios::in | std::ios::out | std::ios::binary);
io.seekp(8, std::ios::beg); // 写到偏移 8
io.write(...);
io.clear(); // 切换方向前清状态
io.seekg(0, std::ios::end); // 再读末尾
六、实用替代:C 风格与 C++17 文件尺寸
// 取文件大小(C++17 filesystem,避免 seek 猜尺寸)
#include <filesystem>
auto sz = std::filesystem::file_size("data.bin");
// C 风格对照:fseek(fp, offset, SEEK_SET|SEEK_CUR|SEEK_END),ftell 取位置
常见需求“读整个文件”不该用 seek:直接 std::ifstream f(path, std::ios::binary); std::string data((std::istreambuf_iterator<char>(f)), {});,或用 filesystem 拿到尺寸后一次性 read。
七、常见问题 FAQ
问:为什么 seekg 后读到乱码?
答:文本模式偏移不合法或切了读写方向没 clear;改成 binary 并在切换前 clear。
问:tellg() 返回 -1?
答:表示流处于失败状态,无法给出位置。先检查 fail()/bad()。
问:能不能只跳读一行、不读整文件?
答:如果行长度不定,只能顺序读;定长记录才可以用 seekg 精确定位,这是随机访问的前提。
核心记三条:① 要精确跳字节就用 ios::binary;② 文本流上的偏移只能是 tellg() 给过的值或 0;③ 任何 seek 之前先 clear() 清状态位。取文件大小交给 std::filesystem::file_size,读整文件别用 seek 估算。
生产内容,同时打通搜索引擎(SEO)与AI搜索引擎(GEO)流量入口的软件! 点击进入365建站器 »
