通过stdin和ostream将数据发送到程序。 (C ++)

时间:2015-11-14 22:36:40

标签: c++ popen ostream filehandle

我想将我的C ++程序中的数据发送到外部管道,如下所示:

FILE* file = popen("my_prog -opt | other_prog", "w");
std::ostream fileStream = some_function(file);
fileStream << "some data";

我知道没有简单的,跨平台的方式来完成第二行,但有没有办法用popen以外的东西来完成同样的事情?我不需要使用popen,但我确实需要使用ostream。它至少需要使用clanggcc进行编译,但最好能与任何编译器一起使用。我也可以改变处理管道的方式,但我没有my_progother_prog的来源。

4 个答案:

答案 0 :(得分:8)

使用std::ostream作为基础目标创建流缓冲区是直接的,并使用这样的流缓冲区创建相应的#include <stdio.h> #include <streambuf> #include <ostream> class stdiobuf : public std::streambuf { enum { bufsize = 2048 }; char buffer[bufsize]; FILE* fp; int (*close)(FILE*); int overflow(int c) { if (c != std::char_traits<char>::eof()) { *this->pptr() = std::char_traits<char>::to_char_type(c); this->pbump(1); } return this->sync() ? std::char_traits<char>::eof() : std::char_traits<char>::not_eof(c); } int sync() { std::streamsize size(this->pptr() - this->pbase()); std::streamsize done(this->fp? fwrite(this->pbase(), 1, size, this->fp): 0); this->setp(this->pbase(), this->epptr()); return size == done? 0: -1; } public: stdiobuf(FILE* fp, int(*close)(FILE*) = fclose) : fp(fp) , close(close) { this->setp(this->buffer, this->buffer + (this->fp? bufsize - 1: 0)); } ~stdiobuf() { this->sync(); this->fp && this->close(this->fp); } }; class opipestream : private virtual stdiobuf , public std::ostream { public: opipestream(std::string const& pipe) : stdiobuf(popen(pipe.c_str(), "w"), pclose) , std::ios(static_cast<std::streambuf*>(this)) , std::ostream(static_cast<std::streambuf*>(this)) { } }; int main() { opipestream out("/usr/bin/sed -e 's/^/xxxx /'"); out << "Hello\n"; out << "world\n"; } 。它大致看起来像这样:

{{1}}

基本思想是您可以通过实现流缓冲区来创建新流。上面的实现应该相当完整。可以改进不完整缓冲区时的错误处理,尽管最可能出现错误的情况是管道已关闭且没有太多可以做的事情。

答案 1 :(得分:3)

由于缺少std::filebuf ctor,我的旧答案仅适用于Windows。由于user4815162342指出替代方案可能正在使用__gnu_cxx::stdio_filebuf<char>

我修补了一些现在可以与windows和linux一起使用的东西,但是你的平台可能无法全部工作。

#ifdef __GNUC__
    #include <ext/stdio_sync_filebuf.h>    
    typedef __gnu_cxx::stdio_sync_filebuf<char> popen_filebuf;
#elif _MSC_VER
    #include<fstream>
    typedef std::filebuf popen_filebuf;
    FILE*(*popen)(const char*, const char*) = _popen;
#else
    static_assert(false, "popen_filebuf is not available for this platform");
#endif

int main()
{
    FILE* file = popen("my_prog -opt | other_prog", "w");

    popen_filebuf buffer(file);
    std::ostream fileStream(&buffer);

    fileStream << "some data";

    return 0;
}

答案 2 :(得分:3)

如果您事先知道您支持哪些平台,则可以使用特定于平台的扩展来从文件描述符中创建iostream。例如,如图here所示,GNU libstdc ++提供__gnu_cxx::stdio_sync_filebuf,可用于从filebuf创建FILE *。继承自std::iostream并使用适当的filebuf对其进行初始化的类(在Linux上使用g ++ 5.2和clang ++ 3.7进行测试)可能如下所示:

#include <iostream>
#include <ext/stdio_sync_filebuf.h>

class StdioStream:
  private __gnu_cxx::stdio_sync_filebuf<char>, public std::iostream {
public:
  explicit StdioStream(FILE *fp):
    __gnu_cxx::stdio_sync_filebuf<char>(fp), std::iostream(this) { }
};

int main()
{
    FILE* file = popen("my_prog -opt | other_prog", "w");
    StdioStream fileStream(file);
    fileStream << "some data";
}

请注意,不能只定义返回some_function的{​​{1}},因为后者具有私有拷贝构造函数,并且因为中间std::ostream需要与流一起被破坏。上面的示例使用多继承能够在filebuf基类之前初始化stdio_sync_filebuf(否则将成为成员) - 有关详细信息,请参阅base-from-member

引用的answer也包含MSVC ++ iostreams的等效代码。

答案 3 :(得分:0)

扩展我对原始问题的评论。

您可以将所有输入保存到磁盘上的文件中,然后将该文件作为输入提供给my_prog

ofstream fileStream("input.txt");
fileStream << "some data";
// insert other input
fileStream.close();
FILE *file = popen("my_prog -opt <input.txt | other_prog", "w");

你可以在这里使用popen以外的东西,可能是直接的system电话。完成所有操作后,您需要删除input.txt文件。