通过 stdin 和 ostream 向程序发送数据。 (C++)
Sending data to a program via stdin and ostream. (C++)
我想将数据从我的 C++ 程序发送到外部管道,如下所示:
FILE* file = popen("my_prog -opt | other_prog", "w");
std::ostream fileStream = some_function(file);
fileStream << "some data";
我知道没有简单的、跨平台的方法来完成第二行,但是有什么方法可以使用 popen
以外的东西来完成同样的事情吗?我不需要使用 popen
,但我确实需要使用 ostream
。它至少需要使用 clang
和 gcc
进行编译,但最好能与任何编译器一起使用。我也可以改变我处理管道的方式,但我没有 my_prog
或 other_prog
.
的来源
由于缺少 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;
}
直接使用 FILE*
作为基础目标创建流缓冲区,并使用这样的流缓冲区创建相应的 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";
}
基本思想是您可以通过实现流缓冲区来创建新流。上面的实现应该是相当完整的。可以改进缓冲区不完整时的错误处理,尽管最可能的错误情况是管道已关闭并且实际上没有什么可以做的。
如果您事先知道自己支持哪些平台,则可以使用特定于平台的扩展从文件描述符创建 iostream。例如,如 here 所示,GNU libstdc++ 提供了 __gnu_cxx::stdio_sync_filebuf
,可用于从 FILE *
创建 filebuf
。 class 继承自 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
那个 returns 一个 std::ostream
因为后者有一个私有复制构造函数,而且因为中间 filebuf
需要随流而毁。上面的示例使用多重继承能够在 iostream
基 class 之前初始化 stdio_sync_filebuf
(否则将是成员)- 详情请参阅 base-from-member。
引用的 answer 还包含 MSVC++ iostream 的等效代码。
我对原始问题的评论的扩展。
您可以将所有输入保存到磁盘上的文件中,然后将该文件作为输入提供给 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
文件。
我想将数据从我的 C++ 程序发送到外部管道,如下所示:
FILE* file = popen("my_prog -opt | other_prog", "w");
std::ostream fileStream = some_function(file);
fileStream << "some data";
我知道没有简单的、跨平台的方法来完成第二行,但是有什么方法可以使用 popen
以外的东西来完成同样的事情吗?我不需要使用 popen
,但我确实需要使用 ostream
。它至少需要使用 clang
和 gcc
进行编译,但最好能与任何编译器一起使用。我也可以改变我处理管道的方式,但我没有 my_prog
或 other_prog
.
由于缺少 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;
}
直接使用 FILE*
作为基础目标创建流缓冲区,并使用这样的流缓冲区创建相应的 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";
}
基本思想是您可以通过实现流缓冲区来创建新流。上面的实现应该是相当完整的。可以改进缓冲区不完整时的错误处理,尽管最可能的错误情况是管道已关闭并且实际上没有什么可以做的。
如果您事先知道自己支持哪些平台,则可以使用特定于平台的扩展从文件描述符创建 iostream。例如,如 here 所示,GNU libstdc++ 提供了 __gnu_cxx::stdio_sync_filebuf
,可用于从 FILE *
创建 filebuf
。 class 继承自 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
那个 returns 一个 std::ostream
因为后者有一个私有复制构造函数,而且因为中间 filebuf
需要随流而毁。上面的示例使用多重继承能够在 iostream
基 class 之前初始化 stdio_sync_filebuf
(否则将是成员)- 详情请参阅 base-from-member。
引用的 answer 还包含 MSVC++ iostream 的等效代码。
我对原始问题的评论的扩展。
您可以将所有输入保存到磁盘上的文件中,然后将该文件作为输入提供给 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
文件。