需继承std::streambuf并重写underflow()(读)和sputn()(写),对a-z/A-Z循环位移、其余字符透传,顺序读写最稳妥,替换rdbuf()后不可依赖seekg()或gcount()。

怎么用 std::streambuf 拦截并修改文件读写内容
核心是继承 std::streambuf,重写 underflow()(读)和 sputn()/overflow()(写),把字节流在进入 std::istream 或 std::ostream 前做位移变换。不是包装 std::ifstream,而是替换它的底层缓冲区。
-
underflow()被调用时,从原始文件读一批字节 → 逐字节加偏移(如 +3)→ 存入内部缓冲区 → 返回首字节指针 -
sputn()更适合写:拿到用户要写的原始数据指针和长度,先位移处理再转发给底层filebuf - 别重写
sync()除非你要强制刷盘逻辑;默认委托给基类即可 - 注意:
std::streambuf不管理内存生命周期,你得自己new[]/delete[]缓冲区,或用std::vector<char></char>管理
位移加密必须处理的边界:非字母字符和模运算
直接对所有字节 +3 会破坏二进制文件(比如 PNG 头变成乱码),也容易让可打印 ASCII 超出范围。实际中几乎没人对整个文件字节做无差别位移——那不是加密,是损坏。
- 只对
a-z、A-Z做循环位移('z' + 1 → 'a'),其他字符原样透传 - 用
(c - 'a' + shift) % 26 + 'a'这类表达式,别写c + shift然后靠char溢出自动绕回——有符号char溢出是未定义行为 - 如果目标是“简单位移”,就别碰 UTF-8 多字节序列;遇到
0x80–0xFF字节一律跳过,否则中文文件一读就崩
为什么不能直接用 fstream 的 rdbuf() 替换后还调 seekg
替换了 rdbuf() 后,seekg() 行为不可靠:你的自定义 streambuf 没实现 seekoff() 和 seekpos(),标准库会退回到失败路径,导致位置错乱或读取重复/跳字。
- 要么完全放弃随机访问,只做顺序读写(最常见、最稳)
- 要么必须重写
seekoff():把逻辑映射回原始文件偏移(比如你缓存了 1024 字节,当前逻辑位置是 500,那原始位置就是base_offset + 500) - 别依赖
gcount()返回值——它返回的是从你的缓冲区“吐出”的字节数,不是原始文件读了多少 - 调试时打印
in.gcount()和in.tellg()对比,很容易发现位置漂移
std::iostream 绑定自定义 streambuf 的三步实操
不是构造时传参,而是先构造空流,再用 rdbuf() 替换,最后关联文件。漏掉任何一步都会静默失败。
立即学习“C++免费学习笔记(深入)”;
- 声明一个继承
std::streambuf的类ShiftBuf,在构造函数里打开原始std::filebuf并保存指针 - 创建
std::istream in(nullptr)(C++11 起允许空初始化),再调in.rdbuf(new ShiftBuf("data.txt")) - 写入同理:
std::ostream out(nullptr); out.rdbuf(new ShiftBuf("out.txt", std::ios::out)) - 注意:
ShiftBuf析构时必须显式关闭内部filebuf,否则文件句柄泄漏 - 示例关键行:
in >> word;此时underflow()自动触发,你已经在里面做了位移
位移本身很简单,难的是让 streambuf 在各种 std::ios 标志(skipws、hex)、异常模式、缓冲区大小变化下不崩——这些细节没文档,全靠看 libstdc++/libc++ 源码里的注释和测试用例。



















