You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于POSIX文件描述符实现std::streambuf的方法与问题解答

问题原因

你的代码写入正常但读取失败,核心原因是对std::streambuf的虚函数调度逻辑理解有误:
xsgetn不是输入流程的必走路径,operator>>调用的非虚函数snextc/sgetc/sbumpc,会先检查内部get区域指针,当get区为空时直接调用虚函数underflow()获取数据。你没有重写underflow,基类默认的underflow直接返回EOF,因此读取流程直接终止,根本不会走到你实现的xsgetn。
另外你写完文件后,文件描述符的偏移量停在文件末尾,就算underflow实现正确,直接调用read也会因为偏移在末尾读到0字节,同样返回EOF。

std::streambuf基类默认处理逻辑

基类已经封装了所有公共非虚接口的调度逻辑,不需要派生类重写非虚函数:

  • 维护get区(读缓冲区)、put区(写缓冲区)的三组指针边界,自动做指针前移、边界检查
  • 所有public读写接口(sgetc/snextc/sputc/sputn等)内部自动转调对应虚函数,默认实现如下:
    • xsgetn:循环调用sbumpc()逐字符读取,直到读满指定长度或碰到EOF
    • xsputn:循环调用sputc()逐字符写入,直到写满指定长度或写入失败
    • underflow():默认返回EOF,标识无数据可读
    • uflow():默认调用underflow(),成功后前移读指针返回字符,失败返回EOF
    • overflow(int c):默认返回EOF,标识无法写入
    • pbackfail(int c):默认返回EOF,标识不支持字符回退
    • sync():默认返回0,不做任何刷盘动作
    • seekoff()/seekpos():默认返回失败,不支持随机寻址
  • 提供setg()/setp()保护方法,供派生类设置自定义读写缓冲区的起止指针。
功能完备的streambuf必须重写的函数

根据支持的能力不同,需要重写的核心虚函数不同:

  • 仅支持输出(配合std::ostream):
    • 必选:重写overflow(int c),处理单字符写入、缓冲区满后的刷盘逻辑,否则逐字符写入场景会直接失败。你之前仅重写xsputn能跑是测试场景刚好走了批量写路径,属于巧合,不是通用实现。
    • 可选优化:重写xsputn实现批量写入,减少虚函数调用开销;重写sync实现用户触发的刷盘逻辑。
  • 仅支持输入(配合std::istream):
    • 必选:重写underflow(),从数据源读取数据填充读缓冲区,是所有读操作的核心入口。
    • 可选优化:重写xsgetn实现批量读取;需要支持字符回退(比如unget)就重写pbackfail;需要随机寻址就重写seekoff/seekpos。
  • 支持双向读写:
    • 同时实现上述输入、输出的必选虚函数,额外处理读写切换时的缓冲区刷新、文件偏移同步逻辑。
代码修复参考

修复两个核心问题:补充underflow实现、写完后重置文件偏移到开头,同时增加简单缓冲区减少系统调用次数:

#include <iostream>
#include <streambuf>
#include <string>
#include <cassert>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>

class fd_streambuf : public std::streambuf
{
    int fd;
    char read_buf[1024];
    char write_buf[1024];
public:
    fd_streambuf(int fileDescriptor) : fd(fileDescriptor)
    {
        setp(write_buf, write_buf + sizeof(write_buf));
    }

    ~fd_streambuf() override
    {
        sync();
        close(fd);
    }

protected:
    int overflow(int c) override
    {
        if (sync() == -1) return EOF;
        if (c != EOF) {
            *pptr() = c;
            pbump(1);
        }
        return c;
    }

    int sync() override
    {
        ssize_t len = pptr() - pbase();
        if (len > 0) {
            ssize_t written = write(fd, pbase(), len);
            if (written != len) return -1;
            setp(write_buf, write_buf + sizeof(write_buf));
        }
        return fsync(fd);
    }

    std::streamsize xsputn(const char* s, std::streamsize n) override
    {
        if (sync() == -1) return 0;
        return write(fd, s, n);
    }

    int underflow() override
    {
        ssize_t n = read(fd, read_buf, sizeof(read_buf));
        if (n <= 0) return EOF;
        setg(read_buf, read_buf, read_buf + n);
        return *gptr();
    }

    std::streamsize xsgetn(char* s, std::streamsize n) override
    {
        std::streamsize total = 0;
        if (gptr() != egptr()) {
            std::streamsize avail = egptr() - gptr();
            std::streamsize copy = std::min(avail, n);
            memcpy(s, gptr(), copy);
            gbump(copy);
            s += copy;
            total += copy;
            n -= copy;
        }
        if (n > sizeof(read_buf)/2) {
            ssize_t r = read(fd, s, n);
            if (r > 0) total += r;
            return total;
        }
        if (n > 0 && underflow() != EOF) {
            std::streamsize avail = egptr() - gptr();
            std::streamsize copy = std::min(avail, n);
            memcpy(s, gptr(), copy);
            gbump(copy);
            total += copy;
        }
        return total;
    }
};

int main()
{
    int fd = open("/tmp/test.txt", O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
    fd_streambuf fd_buf(fd);
    std::ostream os(&fd_buf);
    os << "Hello, world!" << std::endl;
    os.flush();

    lseek(fd, 0, SEEK_SET);

    std::istream is(&fd_buf);
    std::string fileContent;
    is >> fileContent;

    assert(fileContent == "Hello,");
    std::string rest;
    is >> rest;
    assert(rest == "world!");
    return 0;
}

内容的提问来源于stack exchange,提问作者Gian Laager

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 11:27:15