将输入(cin)输出到日志文件(或阻塞)

时间:2009-06-15 19:58:28

标签: c++ io stream

我正在寻找一种方法来分支(tee)从istream(在我的例子中是cin)读取的输入到日志文件(clog / ofstream / etc),同时仍然使用输入进行处理。

我已经阅读过boost :: tee_device,它与我的要求非常相似。不幸的是,它被实现为ostream,因此从“管道的另一侧”解决了类似的问题。

我尝试编写一个istream(adapter)类,它将输入函数转发到包装的输入流(cin),并将读取的内容发送到日志文件。

这适用于直接调用运算符>>(...)的基本类型,但是,我遇到了输入流的一些更高级用法的问题,例如,对于运算符>>(标准: :string)和std :: string getline函数。

有没有更简单的方法(可能通过rdbuf()操作)?

谢谢!

编辑:我可以将我的代码更改为:cin>>值; clog<<值; - 但这将是一个重大而丑陋的变化。我也希望有一个简单的方法来关闭注销。因此,我想用一种方法将其建模为istream“过滤器”,然后简单地用这个istream“logger”替换对cin的所有引用。

理想解决方案:

class log_istream : public std::istream
{
public:
    log_istream( std::istream & in , std::ostream & out );

    /* ... istream forwarding functions ... */

private:
    std::istream & in_;
    std::ostream & out_;     
};

int main() {
    log_istream logger( std::cin , std::ofstream("logfile.out") );

    logger >> value; // this implies infile >> value and logfile << value
    getline(logger,my_string); // this also implies logfile.writeline(value)
    // etc
}

4 个答案:

答案 0 :(得分:3)

使用Boost.IOStreams,您可以定义一个输入过滤器,记录它读入clog的内容。类似的东西:

(警告:未经测试的代码)

class LoggingInputFilter : public multichar_input_filter {
public:
    template<typename Source>
    std::streamsize read(Source& Src, char* S, std::streamsize N)
    {
        streamsize result = read(Src, S, N);
        if (result == -1){
            return result;
        }

        if (std::clog.write(S, result)){
            return result;
        }

        return -1;
    }
};

用std :: cin链接它:

LoggingInputFilter cin_logger;
filtering_stream logged_cin(cin_logger);
logged_cin.push(std::cin);

并使用logged_cin而不是std :: cin

编辑:或者在streabuf级别操作,这样你的代码仍然使用std :: cin:

LoggingInputFilter cin_logger;
filtering_streambuf logged_cin(cin_logger);
logged_cin.push(std::cin.rdbuf());
std::cin.rdbuf(logged_cin);

答案 1 :(得分:1)

我找到了一个简单的解决方案:

Boost :: iostreams在源/接收器过滤器之间提供反转。

虽然tee_filter被建模为接收器,但您可以将它反转()到一个源中,并且它仍然会“发送”过滤到指定接收器的内容:

    boost::iostreams::file log_file("sample.txt", std::ios::trunc); // or std::ios::app

    // Single parameter tee() function returns a tee_filter , and invert() inverts that filter
    boost::iostreams::filtering_istream in(
            boost::iostreams::invert(
                    boost::iostreams::tee(log_file)));

这样,我就登录了所有已过滤的输入。

性能不是问题,但如果有人注意到任何红旗,我会非常感兴趣。感谢。

答案 2 :(得分:1)

最终答案:

#ifndef TEE_ISTREAM_H_
#define TEE_ISTREAM_H_

/*****************************************************************************/

#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/invert.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/tr1/memory.hpp>
#include <iostream>

/*****************************************************************************/

namespace bio = boost::iostreams;

/*****************************************************************************/

class tee_source : public bio::source {
public:
    tee_source( std::istream & in, const std::string & filename )
        : in_(in), log_file_(filename, std::ios::app), tee_(bio::tee(log_file_), 1)
    { }

    std::streamsize read(char* s, std::streamsize n)
    {
        return tee_.read(in_,s,n);
    }

private:
    std::istream &                               in_;
    bio::file                                    log_file_;
    bio::inverse< bio::tee_filter< bio::file > > tee_;
};

/*****************************************************************************/

typedef bio::filtering_istream                tee_istream_t;
typedef std::tr1::shared_ptr< tee_istream_t > tee_istream_ptr_t;

/*****************************************************************************/

inline tee_istream_ptr_t make_tee_istream( std::istream & in, const std::string & filename )
{
    return tee_istream_ptr_t( new tee_istream_t( tee_source( in , filename ), 0 ) );
}

/*****************************************************************************/

#endif

答案 3 :(得分:0)

很好的简单/简短的方式(需要最新的提升我猜测和-std = c ++ 11) - 感谢@mmocny,@ÉricMalenfant和Boost lib devs。

这个演示程序记录到“tmp.log”你在std :: cin和std :: cout上的任何对话:

#include <iostream>

#include <boost/iostreams/tee.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/device/file.hpp>

namespace bio = boost::iostreams;

int main(int, char**)
{
    // Logging
    bio::filtering_ostream log;
    log.push(bio::file_sink("tmp.log", std::ios_base::app));

    // Tee filter instance (will be copied into each filter stream)
    const bio::tee_filter<std::ostream> teeFilter(log);

    // std::out tee
    bio::filtering_ostream out;
    out.push(teeFilter);
    out.push(std::cout);

    // std::in tee
    bio::filtering_istream in;
    in.push(teeFilter, 0); // If you don't pass 0 for buffer size, on std::cin it'll try to read 4096 chars and basically be useless
    in.push(std::cin, 0);

    out << "What is your name ?" << std::endl << std::flush;
    std::string name;
    getline(in, name);
    out << "Hello " << name << std::endl << std::flush;
}