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

C++自定义istream操纵符实现读5跳5字符功能异常排查

问题说明

你需要实现一个自定义istream流操纵符skipchar,核心逻辑为:从输入流中循环读取5个字符、跳过接下来5个字符,直到输入末尾,预期调用方式如下:

string line;
cin >> skipchar >> line;

现有实现无法正常运行,同时要求最终实现不需要引入<sstream>头文件,原有错误代码如下:

struct membuf : streambuf
{
    membuf(char* begin, char* end) {
        this->setg(begin, begin, end);
    }
};
istream& skipchar(istream& stream)
{
    string temp;
    getline(stream, temp);
    char *str = new char[temp.size() + 1];
    strcpy(str, temp.c_str());//get string and convert to char*

    char* res = new char[strlen(str)];
    for (int i=0,j=0;i<strlen(str);i++)
        if ((i / 5) % 2 == 0) //get first 5, then 5 skip, etc
            res[j++] = str[i];

    membuf b(res, res + strlen(res)); //copy str to buffer
    stream.rdbuf(&b);
    return stream;
}
int main()
{
    string str;
    cout << "enter smth:\n";
    cin >> skipchar >> str;
    cout << "entered string: " << str;
    return 0;
}  
原有代码问题分析
  • 局部变量悬空:skipchar函数内定义的membuf对象b是栈上局部变量,函数返回后立刻被销毁,后续流操作访问已释放内存会触发未定义行为。
  • 内存泄漏:new申请的str、res两块堆内存从未被释放,且membuf销毁后没有任何机制回收这部分内存。
  • 字符串构造错误:res数组没有手动添加字符串结束符\0,strlen(res)计算的长度完全不可靠,读取时会出现越界。
  • 流状态破坏:直接替换流的rdbuf会丢失原有流的状态、异常掩码配置,极易触发流错误标记,且提前用getline读完所有输入的逻辑和后续>>运算符的读取逻辑完全不兼容。
正确实现方案

不需要提前把所有输入读入字符串再构造自定义缓冲区,直接实现一层过滤流缓冲区即可,全程不需要引入<sstream>头文件:

#include <iostream>
#include <string>
#include <streambuf>
using namespace std;

class skipfilterbuf : public streambuf {
private:
    streambuf* src;
    int cnt = 0;
    enum Mode { READ, SKIP } mode = READ;
    char cur_ch;
protected:
    int underflow() override {
        while (true) {
            int c = src->sbumpc();
            if (c == EOF) return EOF;

            if (mode == READ) {
                cur_ch = static_cast<char>(c);
                setg(&cur_ch, &cur_ch, &cur_ch + 1);
                cnt++;
                if (cnt == 5) {
                    cnt = 0;
                    mode = SKIP;
                }
                return c;
            } else {
                cnt++;
                if (cnt == 5) {
                    cnt = 0;
                    mode = READ;
                }
            }
        }
    }
public:
    skipfilterbuf(streambuf* s) : src(s) {}
};

istream& skipchar(istream& stream) {
    static int buf_idx = ios_base::xalloc();
    if (stream.pword(buf_idx) == nullptr) {
        auto* filter_buf = new skipfilterbuf(stream.rdbuf());
        stream.pword(buf_idx) = filter_buf;
        stream.rdbuf(filter_buf);
        // 注册流销毁回调,自动释放过滤缓冲区避免内存泄漏
        stream.register_callback([](ios_base::event ev, ios_base& s, int idx) {
            if (ev == ios_base::erase_event) {
                delete static_cast<skipfilterbuf*>(s.pword(idx));
                s.pword(idx) = nullptr;
            }
        }, buf_idx);
    }
    return stream;
}

int main()
{
    string str;
    cout << "enter smth:\n";
    cin >> skipchar >> str;
    cout << "entered string: " << str << endl;
    return 0;
}
实现说明
  • 核心过滤逻辑封装在skipfilterbuf中,继承标准streambuf,逐字符从原输入流读取数据,自动按照读5个、跳5个的规则过滤,不需要提前缓存全量输入。
  • 用ios_base::xalloc()申请流的私有存储位存放过滤缓冲区指针,避免局部变量生命周期结束后出现悬空指针问题。
  • 注册流事件回调,在流销毁时自动释放申请的过滤缓冲区内存,无内存泄漏问题。
  • 过滤逻辑在流缓冲区层生效,后续不管用>>读取字符串还是用getline读取行,都会自动应用跳字符规则,兼容性更强。
  • 测试验证:输入0123456789abcdefghij时,最终读取到的结果为01234abcde,完全符合读5跳5的循环逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 21:57:37