【问题标题】:inserting text before each line using std::ostream使用 std::ostream 在每行之前插入文本
【发布时间】:2014-07-04 12:04:48
【问题描述】:

我想知道是否可以从 std::ostream 继承,并以将一些信息(例如行号)添加到每行开头的方式覆盖 flush()。然后我想通过 rdbuf() 将它附加到 std::ofstream (或 cout),这样我就得到了这样的东西:

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

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

将其放入 file.txt

1 this is the first line.
2 this is the second line.

【问题讨论】:

    标签: c++ stl iostream


    【解决方案1】:

    flush() 不会是在这种情况下覆盖的函数,尽管你在正确的轨道上。您应该在底层std::streambuf 接口上重新定义overflow()。例如:

    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;
    };
    

    现在你可以这样做了:

    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

    【讨论】:

    • 此解决方案可用于输出到 sstream 吗?
    • @MattMcNabb 不是真的;缓冲区是std::filebuf 的包装器,因此它只能用于文件。如果你有多种类型的流你想让它工作,使用 std::streambuf* 并通过构造函数使用流的缓冲区对其进行初始化。
    • 如果您将 std::filebuf m_sbuf; 替换为 std::streambuf* m_sbuf;,这可能是 James Kanze 风格的流缓冲过滤器。
    • 一个后续问题:我用std::endl之后好像什么都没写。我是否需要像对待operator&lt;&lt;(ostream&amp; (*func)(ostream&amp;)) 一样单独对待这些?
    • @Johann 除了将\n 写入流之外,std::endl 还会刷新流。所以我会避免使用那个操纵器,除非你必须这样做。
    【解决方案2】:

    James Kanze 的classic article on Filtering Streambufs 有一个非常相似的例子,它在每行的开头放置一个时间戳。您可以修改该代码。

    或者,您可以使用从那篇文章中的想法衍生而来的 Boost 工具。

    #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";
    }
    

    【讨论】:

    • 谢谢你。 Boost 将简化我希望在运行时更改过滤选项的情况。我将不得不看看 filter_ostream 是否可以作为 shared_ptr 成员变量存储在类中。
    • 是的,filtering_ostream 继承 std::ostream(并且两者都有公共虚拟析构函数)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    • 2012-11-17
    • 2023-01-18
    • 1970-01-01
    相关资源
    最近更新 更多