Создание собственного манипулятора istream

Я хочу сделать заказ istream манипулятор, который читает 2 символа из ввода, затем пропускает 2 символа из ввода и делает это до тех пор, пока не закончится какой-либо ввод.

Например, если у меня есть такой код:

std::string str;
std::cin >> skipchar >> str;

куда skipchar мой манипулятор, если пользователь входит 1122334455, str должен содержать 113355,

Это то, что я получил до сих пор, я не знаю, что я должен поместить в условие цикла while, чтобы этот код работал правильно:

istream& skipchar(istream& stream)
{
    char c;

    while(1)
    {
        for (int i = 0; i < 2; ++i)
            stream >> c;

        for (int i = 0; i < 2; ++i)
            stream.ignore(1, '\0');
    }

    return stream;
}

Любая помощь будет оценена.

2 ответа

Решение

Это очень хороший вопрос. Я не знаю, возможно ли это. Но я реализовал что-то другое, что дает вам тот же короткий синтаксис, который вы хотели, перегружая >> operator с новым классом под названием Skip2, Вот код (который мне очень понравилось писать!:-))

#include <iostream>
#include <string>
#include <istream>
#include <sstream>

using namespace std;

class Skip2 {
public:
    string s;
};

istream &operator>>(istream &s, Skip2 &sk) 
{
    string str;
    s >> str;

    // build new string
    ostringstream build;
    int count = 0;
    for (char ch : str) {
        // a count "trick" to make skip every other 2 chars concise
        if (count < 2) build << ch;
        count = (count + 1) % 4;
    }

    // assign the built string to the var of the >> operator
    sk.s = build.str();

    // and of course, return this istream
    return s;
}



int main()
{
    istringstream s("1122334455");
    Skip2 skip;

    s >> skip;
    cout << skip.s << endl;

    return 0;
}

Это сложно; Манипуляторы istream работают не как "фильтры" в потоке, а как одиночные операции. Манипуляторы istream, предусмотренные Стандартом (noskipws, hex и т. д.) выполняют свою работу, устанавливая и снимая флажки в потоке, чтобы они отображали только те функции, которые уже доступны.

Тем не менее, можно создать фильтрующую потоковую буферизацию, охватывающую потоковую буферизацию cin (или любой поток ввода) и используйте манипулятор для его установки или удаления:

struct skipbuf : std::streambuf {
    std::unique_ptr<std::streambuf> src;
    int i;
    char buf[4];
    skipbuf(std::streambuf* src) : std::streambuf{*src}, src{src} {
        setg(buf, buf + 2, buf + 2);
    }
    std::streambuf* unwrap() {
        while (buf + i != gptr())
            src->sputbackc(buf[--i]);
        return src.release();
    }
    std::streambuf::int_type underflow() override {
        setg(buf, buf, buf + std::min(i = src->sgetn(buf, 4), 2));
        return i ? buf[0] : traits_type::eof();
    }
};

std::istream& skipchar(std::istream& is) {
    is.rdbuf(new skipbuf{is.rdbuf()});
    return is;
}

std::istream& noskipchar(std::istream& is) {
    if (auto* buf = dynamic_cast<skipbuf*>(is.rdbuf()))
        delete (is.rdbuf(buf->unwrap()), buf);
    return is;
}

Пример использования:

int main() {
    std::istringstream iss{"1122334455   hello"};
    std::string s1, s2;
    iss >> skipchar >> s1 >> noskipchar >> s2;
    std::cout << s1 << ' ' << s2 << std::endl;
}

Ожидаемый результат ( запустите его онлайн):

113355 hello
Другие вопросы по тегам