Отправка данных в программу через stdin и ostream. (С++)
Я хотел бы отправить данные из моей программы на 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
.
4 ответов
это прямо вперед, чтобы создать буфер потока с помощью 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";
}
основная идея заключается в том, что вы можете создать новый поток по реализации буфера потока. Вышеуказанное осуществление должно быть достаточно полным. Обработка ошибок, когда неполный буфер может быть улучшен, хотя наиболее вероятным случаем ошибки является то, что канал был закрыто, и на самом деле мало что можно сделать.
мой старый ответ работал только под окнами из-за отсутствия std::filebuf
ctor. As 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;
}
если вы заранее знаете, какие платформы вы поддерживаете, вы можете использовать расширения платформы для создания iostream из файлового дескриптора. Например, как показано здесь, GNU libstdc++ предоставляет __gnu_cxx::stdio_sync_filebuf
который можно использовать для создания filebuf
С FILE *
. Класс, наследующий от std::iostream
и инициализирует его соответствующим filebuf
, протестировано с g++ 5.2 и clang++ 3.7 на Linux, может выглядеть так:
#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
что возвращает std::ostream
, потому что последний имеет собственный конструктор копирования, и потому, что средний filebuf
необходимо уничтожить вместе с потоком. В приведенном выше примере используется множественное наследование для инициализации stdio_sync_filebuf
(который в противном случае был бы член) перед iostream
базовый класс - см. база-от членов для сведения.
ссылки ответ также содержит эквивалентный код для MSVC++ iostreams.
расширение моего комментария к исходному вопросу.
вы можете сохранить все входные данные в файл на диске, а затем передать этот файл в качестве входных данных в 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");
вы могли бы использовать что-то другое, чем попен здесь, возможно, прямой system
звонок. Как только вы закончите, вы захотите удалить .