Получение FILE * из std :: fstream
Есть (кросс-платформенный) способ получить C FILE * дескриптор из C ++ std :: fstream?
Причина, по которой я спрашиваю, заключается в том, что моя библиотека на C ++ принимает fstream и в одной конкретной функции, я хотел бы использовать библиотеку C, которая принимает FILE *.
- Чтение строки из текстового файла и перенос строк в вектор?
- Как использовать разделители без дефолта при чтении текстового файла с помощью std :: fstream?
- В C ++ есть ли способ перейти к определенной строке в текстовом файле?
- Как построить c ++ fstream из дескриптора файла POSIX?
- Как написать в середине файла в C ++?
- Как читать растущий текстовый файл на C ++?
- mmap () против блоков чтения
- std :: fstream буферизация против ручного буферизации (почему 10-кратное усиление с ручной буферизацией)?
- fstream не создаст файл
- мне нужно закрыть std :: fstream?
- С помощью Cream filestreams (fstream), как вы можете определить размер файла?
- std :: fstream не создает файл
- Как читать строки за строкой или весь текстовый файл сразу?
Нет стандартизированного способа. Я предполагаю, что это связано с тем, что группа стандартизации C ++ не хотела предполагать, что дескриптор файла может быть представлен как fd.
Большинство платформ, похоже, предоставляют некоторые нестандартные способы сделать это.
http://www.ginac.de/~kreckel/fileno/ обеспечивает хорошую запись ситуации и предоставляет код, который скрывает всю грубость платформы, по крайней мере для GCC. Учитывая, насколько важен это только на GCC, я думаю, что я избег бы сделать это вместе, если это возможно.
ОБНОВЛЕНИЕ: см. @Jettatura, что я думаю, что это лучший ответ https://stackoverflow.com/a/33612982/225186 (только Linux?).
ОРИГИНАЛ:
(Возможно, не перекрестная платформа, но простая)
Упрощение взлома в http://www.ginac.de/~kreckel/fileno/ (ответ dvorak) и просмотр этого расширения gcc http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/ api / a00069.html # a59f78806603c619eafcd4537c920f859 , у меня есть это решение, которое работает на GCC
(по крайней мере, 4.8) и clang
(по крайней мере 3.3)
#include #include typedef std::basic_ofstream::__filebuf_type buffer_t; typedef __gnu_cxx::stdio_filebuf io_buffer_t; FILE* cfile_impl(buffer_t* const fb){ return (static_cast(fb))->file(); //type std::__c_file } FILE* cfile(std::ofstream const& ofs){return cfile_impl(ofs.rdbuf());} FILE* cfile(std::ifstream const& ifs){return cfile_impl(ifs.rdbuf());}
и его можно использовать,
int main(){ std::ofstream ofs("file.txt"); fprintf(cfile(ofs), "sample1"); fflush(cfile(ofs)); // ofs << std::flush; doesn't help ofs << "sample2\n"; }
Ограничения: (комментарии приветствуются)
-
Я считаю, что важно
fflush
после печатиfprintf
вstd::ofstream
, иначе «sample2» появляется перед «sample1» в примере выше. Я не знаю, есть ли лучшее решение для этого, чем использованиеfflush
. Примечательно, чтоofs << flush
не помогает. -
Невозможно извлечь FILE * из
std::stringstream
, я даже не знаю, возможно ли это. (см. ниже для обновления). -
Я до сих пор не знаю, как извлечь
stderr
изstd::cerr
и т. Д., Например, для использования вfprintf(stderr, "sample")
в гипотетическом коде, таком какfprintf(cfile(std::cerr), "sample")
.
Что касается последнего ограничения, единственным обходным решением, которое я нашел, является добавление этих перегрузок:
FILE* cfile(std::ostream const& os){ if(std::ofstream const* ofsP = dynamic_cast(&os)) return cfile(*ofsP); if(&os == &std::cerr) return stderr; if(&os == &std::cout) return stdout; if(&os == &std::clog) return stderr; if(dynamic_cast(&os) != 0){ throw std::runtime_error("don't know cannot extract FILE pointer from std::ostringstream"); } return 0; // stream not recognized } FILE* cfile(std::istream const& is){ if(std::ifstream const* ifsP = dynamic_cast(&is)) return cfile(*ifsP); if(&is == &std::cin) return stdin; if(dynamic_cast(&is) != 0){ throw std::runtime_error("don't know how to extract FILE pointer from std::istringstream"); } return 0; // stream not recognized }
Попытка обработки iostringstream
Можно читать fscanf
из fscanf
с помощью fmemopen
, но для этого требуется много учета и обновления входной позиции streamа после каждого чтения, если вы хотите объединить С-чтения и чтения на C ++. Я не смог преобразовать это в функцию cfile
как cfile
выше. (Возможно, class cfile
который продолжает обновлять после каждого чтения, - это путь).
// hack to access the protected member of istreambuf that know the current position char* access_gptr(std::basic_streambuf>& bs){ struct access_class : std::basic_streambuf>{ char* access_gptr() const{return this->gptr();} }; return ((access_class*)(&bs))->access_gptr(); } int main(){ std::istringstream iss("11 22 33"); // read the C++ way int j1; iss >> j1; std::cout << j1 << std::endl; // read the C way float j2; char* buf = access_gptr(*iss.rdbuf()); // get current position size_t buf_size = iss.rdbuf()->in_avail(); // get remaining characters FILE* file = fmemopen(buf, buf_size, "r"); // open buffer memory as FILE* fscanf(file, "%f", &j2); // finally! iss.rdbuf()->pubseekoff(ftell(file), iss.cur, iss.in); // update input stream position from current FILE position. std::cout << "j2 = " << j2 << std::endl; // read again the C++ way int j3; iss >> j3; std::cout << "j3 = " << j3 << std::endl; }
Ну, вы можете получить дескриптор файла – я забыл, является ли метод fd () или getfd (). Реализации, которые я использовал, предоставляют такие методы, но стандарт языка не требует их, я считаю, стандарту не должно волновать, использует ли ваша платформа fd для файлов.
Из этого вы можете использовать fdopen (fd, mode), чтобы получить FILE *.
Тем не менее, я думаю, что механизмы, требуемые стандартом для синхронизации STDIN / cin, STDOUT / cout и STDERR / cerr, не должны быть вам видны. Поэтому, если вы используете как fstream, так и FILE *, буферизация может помешать вам.
Кроме того, если либо фид ИЛИ ФАЙЛ закрывается, они, вероятно, закрывают базовый fd, поэтому вам нужно убедиться, что вы запустили BOTH перед закрытием EITHER.
В однопоточном приложении POSIX вы можете легко получить номер fd переносимым образом:
int fd = dup(0); close(fd); // POSIX requires the next opened file descriptor to be fd. std::fstream file(...); // now fd has been opened again and is owned by file
Этот метод ломается в многопоточном приложении, если этот код запускается с другими streamами, открывая дескрипторы файлов.
еще один способ сделать это в Linux:
#include #include template struct STDIOAdapter { static FILE* yield(STREAM* stream) { assert(stream != NULL); static cookie_io_functions_t Cookies = { .read = NULL, .write = cookieWrite, .seek = NULL, .close = cookieClose }; return fopencookie(stream, "w", Cookies); } ssize_t static cookieWrite(void* cookie, const char* buf, size_t size) { if(cookie == NULL) return -1; STREAM* writer = static_cast (cookie); writer->write(buf, size); return size; } int static cookieClose(void* cookie) { return EOF; } }; // STDIOAdapter
Использование, например:
#include #include #include using namespace boost::iostreams; int main() { filtering_ostream out; out.push(boost::iostreams::bzip2_compressor()); out.push(file_sink("my_file.txt")); FILE* fp = STDIOAdapter::yield(&out); assert(fp > 0); fputs("Was up, Man", fp); fflush (fp); fclose(fp); return 1; }
Пожалуйста, посмотрите на эту библиотеку
MDS utils
Он решает проблему, потому что позволяет обрабатывать C FILE * как stream C ++. Он использует библиотеки Boost C ++. Вы должны использовать Doxygen для просмотра документации.
Существует способ получить файловый дескриптор из fstream
а затем преобразовать его в FILE*
(через fdopen
). Лично я не вижу необходимости в FILE*
, но с файловым дескриптором вы можете делать много интересного, например redirect ( dup2
).
Решение:
#define private public #define protected public #include #undef private #undef protected std::ifstream file("some file"); auto fno = file._M_filebuf._M_file.fd();
Последняя строка работает для libstdc ++. Если вы используете какую-то другую библиотеку, вам нужно немного ее перестроить.
Этот трюк грязный и выведет всех частных и публичных членов fstream. Если вы хотите использовать его в своем производственном коде, я предлагаю вам создать отдельные .cpp
и .h
с помощью одной функции int getFdFromFstream(std::basic_ios
, Файл заголовка не должен включать fstream.