9

如何确定天气 ostream 是文件还是控制台流。在下面的程序中,我想打印“Hello file!” 在写入文件和“你好控制台!”时 在写入控制台时。我应该在第 17 行指定什么条件?

#include <fstream>
#include<iostream>
#include <string>
using namespace std;

class A{
public:
        A(string msg):_str(msg){}
        string str()const {return _str;};
private:
        string _str;
};

ostream & operator << (ostream & os, const A & a)
{
        if (os is ofstream) //this is line 17
                os << "Hello file! " << a.str() << endl;
        else
                os << "Hello console! " << a.str() << endl;

        return os;
}

int main()
{
        A a("message");
        ofstream ofile("test.txt");
        if (!ofile)
                cerr << "Unable to open file";
        else
                ofile << a;  // "Hello file"

        cout << a << endl; // "Hello console"
}
4

6 回答 6

4

您可以 (ab)use tellp()-1如果流没有位置则返回:

bool isConsoleStream(ostream const& stream)
{
    return stream.tellp() == -1;
}

当然,可能还有其他流-1为此函数返回,因此请谨慎使用。

于 2013-08-06T13:34:57.103 回答
4

也许不漂亮,但是

std::streambuf const * coutbuf = std::cout.rdbuf();
std::streambuf const * cerrbuf = std::cerr.rdbuf();

ostream & operator << (ostream & os, const A & a)
{
        std::streambuf const * osbuf = os.rdbuf();

        if ( osbuf == coutbuf || osbuf == cerrbuf )
                os << "Hello console! " << a.str() << endl;
        else
                os << "Hello file! " << a.str() << endl;

        return os;
}

我们可以使用&os == &std::cout,但标准输出可能会被重定向到文件,所以我认为最好使用 streambuf 对象。(请参阅此答案以更好地了解重定向的工作原理,以及为什么比较 streambuf 可以安全地解决问题!)

于 2013-08-06T13:28:52.313 回答
3

没有便携的方法。在 Unix 下,你可以这样做:

if ( (&os == &std::cout && isatty( STDOUT ))
        || (&os == &std::cerr && isatty( STDERR ))
        || (&os == &std::clog && isatty( STDERR )) ) }
    //  is a terminal...
}

在 Windows 下,isatty变为_isatty,我不确定宏是否存在(但我怀疑它们确实存在)。

当然,这假设您不会在代码中混淆它。就像是:

std::ostream s( std::cout.rdbuf() );

例如,或者:

std::cout.rdbuf( &someFileBuf );

甚至:

std::ofstream s( "/dev/tty" );  //  (or "CONS" under Windows).

但它几乎与您在没有实际fd 来自filebuf.

于 2013-08-06T13:50:05.717 回答
2

一个是a ofstream,另一个是a ostream。只要有两种方法。

#include <iostream>
#include <string>
#include <fstream>

class A {
    std::string s;
public:
    A(const std::string& s) : s(s){}
    std::string str() const {return s;}
};


ostream & operator << (std::ostream & os, const A & a)
{
    return os << "console: " << a.str() << std::endl;
}

ofstream & operator << (std::ofstream & os, const A & a)
{
    return os << "file: " << a.str() << std::endl;
}

int main()
{
    A a("hello world");
    std::cout << a << endl;
}
于 2013-08-06T13:43:11.537 回答
0

这适用于 Visual Studio 2012

if (typeid(os) == typeid(ofstream)) //this is line 17

但是 anostream可能不是 ofstream 或控制台,因此您必须小心。

于 2013-08-06T13:44:00.363 回答
0

检查 C++ 字符流是否连接到终端/控制台/tty 的函数。

理想情况下,我们将使用位于 C++ stdio 流(cin、cout、cerr 或 clog)的流缓冲区之下的文件描述符。但是,无法检索底层文件描述符。因此,我们使用的事实是,在程序启动时,stdio 流缓冲区连接到程序的标准输入和输出。

此功能仅在以下情况下有效:

  1. 启动 C++ stdio 流的流缓冲区不得更改。因为启动 C++ stdio 流的流缓冲区的地址被用作标识符。例如,通过删除它们,然后分配一个新的流缓冲区,该缓冲区与启动 C++ stdio 流的这些流缓冲区之一具有相同的地址。

  2. 程序启动后,程序的 stdio 不得改变。因为 stdio 流缓冲区的 TTY 状态是在程序启动时存储的。例如,如果在启动时 std. out 连接到一个终端,然后它被程序外部的东西重定向到管道或文件。[您可以在运行时检索它们,而不是在启动时存储 TTY 状态,但是您必须确保您的程序(以及它使用的所有库)不会更改 stdio 文件描述符(0、1 和 2) . 请记住,stdio 流缓冲区很可能使用其他(重复)文件描述符。]

代码:

#include <iostream>
extern "C" {
#ifdef _WIN32
# include <io.h>        // for: _isatty()
#else
# include <unistd.h>    // for: isatty()
#endif
}

// Stdio file descriptors.
#ifndef STDIN_FILENO
# define STDIN_FILENO   0
# define STDOUT_FILENO  1
# define STDERR_FILENO  2 
#endif


// Store start-up addresses of C++ stdio stream buffers as identifiers.
// These addresses differ per process and must be statically linked in.
// Assume that the stream buffers at these stored addresses
//  are always connected to their underlaying stdio files.
static const  streambuf* const StdioBufs[] = {
    std::cin.rdbuf(),  std::cout.rdbuf(),  std::cerr.rdbuf(),  std::clog.rdbuf()
};
static const wstreambuf* const StdioWBufs[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
    std::wcin.rdbuf(), std::wcout.rdbuf(), std::wcerr.rdbuf(), std::wclog.rdbuf()
};

// Store start-up terminal/TTY statuses of C++ stdio stream buffers.
// These statuses differ per process and must be statically linked in.
// Assume that the statuses don't change during the process life-time.
static const bool StdioTtys[sizeof(StdioBufs)/sizeof(StdioBufs[0])] = {
#ifdef _WIN32
    _isatty(STDIN_FILENO), _isatty(STDOUT_FILENO), _isatty(STDERR_FILENO), _isatty(STDERR_FILENO)
#else
     isatty(STDIN_FILENO),  isatty(STDOUT_FILENO),  isatty(STDERR_FILENO),  isatty(STDERR_FILENO)
#endif
};

// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio chararacter streams: cin, cout, cerr and clog.
bool isTTY(const ios& strm)
{
    for(unsigned int i = 0; i < sizeof(StdioBufs)/sizeof(StdioBufs[0]); ++i) {
        if(strm.rdbuf() == StdioBufs[i])
            return StdioTtys[i];
    }
    return false;
}

// Is a Terminal/Console/TTY connected to the C++ stream?
// Use on C++ stdio wide-chararacter streams: wcin, wcout, wcerr and wclog.
bool isTTY(const wios& strm)
{
    for(unsigned int i = 0; i < sizeof(StdioWBufs)/sizeof(StdioWBufs[0]); ++i) {
        if(strm.rdbuf() == StdioWBufs[i])
            return StdioTtys[i];
    }
    return false;
}

注意:我只在 Linux 上测试过。

于 2018-08-02T18:29:27.580 回答