2014-05-15 33 views
1

我想知道是否有可能從std :: ostream繼承,並以某種方式重寫flush(),以便某些信息(比如行號)被添加到每行的開頭。然後我想將它連接到一個std :: ofstream的(或COUT)通過rdbuf(),使我得到這樣的:在每行前插入文本使用std :: ostream

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. 

回答

4

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

+1

該解決方案是否可用於輸出到sstream? –

+1

@MattMcNabb不是真的;緩衝區是一個'std :: filebuf'的包裝,所以它只能用於文件。如果您有多種類型的流需要使用它,請使用'std :: streambuf *'並使用流的緩衝區通過構造函數初始化它。 – 0x499602D2

+1

如果你用'std :: streambuf * m_sbuf;'替換'std :: filebuf m_sbuf;',這可能是James Kanze風格的streambuf過濾器。 – aschepler

2

詹姆斯甘孜的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"; 
} 
+0

謝謝你。 Boost會簡化我希望在運行時更改篩選選項的情況。我將不得不查看是否可以將filtering_ostream存儲爲類中的shared_ptr成員變量。 – Johann

+1

是的,'filtering_ostream'繼承'std :: ostream'(並且都有公共虛擬析構函數)。 – aschepler

相關問題