문제

I would like to know if it is possible to inherit from std::ostream, and to override flush() in such a way that some information (say, the line number) is added to the beginning of each line. I would then like to attach it to a std::ofstream (or cout) through rdbuf() so that I get something like this:

ofstream fout("file.txt");
myostream os;
os.rdbuf(fout.rdbuf());

os << "this is the first line.\n";
os << "this is the second line.\n";

would put this into file.txt

1 this is the first line.
2 this is the second line.
도움이 되었습니까?

해결책

flush() wouldn't be the function to override in this context, though you're on the right track. You should redefine overflow() on the underlying std::streambuf interface. For example:

class linebuf : public std::streambuf
{
public:
    linebuf() : m_sbuf() { m_sbuf.open("file.txt", std::ios_base::out); }

    int_type overflow(int_type c) override
    {
        char_type ch = traits_type::to_char_type(c);
        if (c != traits_type::eof() && new_line)
        {
            std::ostream os(&m_sbuf);
            os << line_number++ << " ";
        }

        new_line = (ch == '\n');
        return m_sbuf.sputc(ch);
    }

    int sync() override { return m_sbuf.pubsync() ? 0 : -1; }
private:
    std::filebuf m_sbuf;
    bool new_line = true;
    int line_number = 1;
};

Now you can do:

linebuf buf;
std::ostream os(&buf);

os << "this is the first line.\n";  // "1 this is the first line."
os << "this is the second line.\n"; // "2 this is the second line."

Live example

다른 팁

James Kanze's classic article on Filtering Streambufs has a very similar example which puts a timestamp at the beginning of every line. You could adapt that code.

Or, you could use the Boost tools that grew out of the ideas in that article.

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/array.hpp>
#include <cstring>
#include <limits>

// line_num_filter is a model of the Boost concept OutputFilter which
// inserts a sequential line number at the beginning of every line.
class line_num_filter
    : public boost::iostreams::output_filter
{
public:
    line_num_filter();

    template<typename Sink>
    bool put(Sink& snk, char c);

    template<typename Device>
    void close(Device&);

private:
    bool m_start_of_line;
    unsigned int m_line_num;
    boost::array<char, std::numeric_limits<unsigned int>::digits10 + 4> m_buf;
    const char* m_buf_pos;
    const char* m_buf_end;
};

line_num_filter::line_num_filter() :
    m_start_of_line(true),
    m_line_num(1),
    m_buf_pos(m_buf.data()),
    m_buf_end(m_buf_pos)
{}

// put() must return true if c was written to dest, or false if not.
// After returning false, put() with the same c might be tried again later.
template<typename Sink>
bool line_num_filter::put(Sink& dest, char c)
{
    // If at the start of a line, print the line number into a buffer.
    if (m_start_of_line) {
        m_buf_pos = m_buf.data();
        m_buf_end = m_buf_pos +
            std::snprintf(m_buf.data(), m_buf.size(), "%u ", m_line_num);
        m_start_of_line = false;
    }

    // If there are buffer characters to be written, write them.
    // This can be interrupted and resumed if the sink is not accepting
    // input, which is why the buffer and pointers need to be members.
    while (m_buf_pos != m_buf_end) {
        if (!boost::iostreams::put(dest, *m_buf_pos))
            return false;
        ++m_buf_pos;
    }

    // Copy the actual character of data.
    if (!boost::iostreams::put(dest, c))
        return false;

    // If the character copied was a newline, get ready for the next line.
    if (c == '\n') {
        ++m_line_num;
        m_start_of_line = true;
    }
    return true;
}

// Reset the filter object.
template<typename Device>
void line_num_filter::close(Device&)
{
    m_start_of_line = true;
    m_line_num = 1;
    m_buf_pos = m_buf_end = m_buf.data();
}


int main() {
    using namespace boost::iostreams;
    filtering_ostream myout;
    myout.push(line_num_filter());
    myout.push(std::cout);

    myout << "this is the first line.\n";
    myout << "this is the second line.\n";
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top