txt texto que programa por manejo linea leer lea imprimir definicion crear como buscar binarios archivos archivo c++ posix fstream file-descriptor

texto - manejo de archivos en c++ fstream



¿Cómo construir un c++ fstream desde un descriptor de archivo POSIX? (7)

Básicamente estoy buscando una versión C ++ de fdopen (). Hice un poco de investigación sobre esto y es una de esas cosas que parece que debería ser fácil, pero resulta ser muy complicado. ¿Me estoy perdiendo algo en esta creencia (es decir, realmente es fácil)? Si no, ¿hay una buena biblioteca en algún lado para manejar esto?

EDITAR: Moví mi solución de ejemplo a una respuesta separada.


AFAIK, no hay forma de hacer esto en C ++ estándar. Dependiendo de su plataforma, su implementación de la biblioteca estándar puede ofrecer (como una extensión no estándar) un constructor fstream que tome un descriptor de archivo (Este es el caso de libstdc ++, IIRC) o un FILE* como entrada.

Otra alternativa sería utilizar un dispositivo boost::iostreams::file_descriptor , que podría boost::iostreams::file_descriptor en un boost::iostreams::stream si desea tener una interfaz std :: stream para él.


De la respuesta dada por Éric Malenfant:

AFAIK, no hay forma de hacer esto en C ++ estándar. Dependiendo de su plataforma, su implementación de la biblioteca estándar puede ofrecer (como una extensión no estándar) un constructor fstream que tome un descriptor de archivo como entrada. (Este es el caso de libstdc ++, IIRC) o un ARCHIVO *.

En base a las observaciones anteriores y mi investigación a continuación, hay código de trabajo en dos variantes; uno para libstdc ++ y otro para Microsoft Visual C ++.

libstdc ++

Hay una plantilla de clase __gnu_cxx::stdio_filebuf no estándar que hereda std::basic_streambuf y tiene el siguiente constructor

stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))

con descripción Este constructor asocia un buffer de flujo de archivos con un descriptor de archivo POSIX abierto.

Lo creamos pasando el identificador POSIX (línea 1) y luego lo pasamos al constructor de istream como basic_streambuf (línea 2):

#include <ext/stdio_filebuf.h> #include <iostream> #include <fstream> #include <string> using namespace std; int main() { ofstream ofs("test.txt"); ofs << "Writing to a basic_ofstream object..." << endl; ofs.close(); int posix_handle = fileno(::fopen("test.txt", "r")); __gnu_cxx::stdio_filebuf<char> filebuf(posix_handle, std::ios::in); // 1 istream is(&filebuf); // 2 string line; getline(is, line); cout << "line: " << line << std::endl; return 0; }

Microsoft Visual C ++

Solía ​​haber una version no estándar del constructor de ifstream que toma el descriptor de archivo POSIX, pero falta tanto de current documentos current como del código. Hay otra versión no estándar del constructor de ifstream que toma FILE *

explicit basic_ifstream(_Filet *_File) : _Mybase(&_Filebuffer), _Filebuffer(_File) { // construct with specified C stream }

y no está documentado (ni siquiera pude encontrar documentación antigua donde estaría presente). Lo llamamos (línea 1) con el parámetro que es el resultado de llamar a _fdopen para obtener C stream FILE * del manejador de archivo POSIX.

#include <cstdio> #include <iostream> #include <fstream> #include <string> using namespace std; int main() { ofstream ofs("test.txt"); ofs << "Writing to a basic_ofstream object..." << endl; ofs.close(); int posix_handle = ::_fileno(::fopen("test.txt", "r")); ifstream ifs(::_fdopen(posix_handle, "r")); // 1 string line; getline(ifs, line); ifs.close(); cout << "line: " << line << endl; return 0; }



Existe una buena posibilidad de que el compilador ofrezca un constructor de fstream basado en FILE, aunque no sea estándar. Por ejemplo:

FILE* f = fdopen(my_fd, "a"); std::fstream fstr(f); fstr << "Greetings/n";

Pero hasta donde sé, no hay una forma portátil de hacerlo.


Parte de la motivación original (no explícita) de esta pregunta es tener la capacidad de pasar datos entre programas o entre dos partes de un programa de prueba utilizando un archivo temporal creado de manera segura, pero tmpnam () arroja una advertencia en gcc, por lo que quería para usar mkstemp () en su lugar. Aquí hay un programa de prueba que escribí basado en la respuesta dada por Éric Malenfant pero usando mkstemp () en lugar de fdopen (); esto funciona en mi sistema Ubuntu con las bibliotecas de Boost instaladas:

#include <stdlib.h> #include <string.h> #include <assert.h> #include <string> #include <iostream> #include <boost/filesystem.hpp> #include <boost/iostreams/device/file_descriptor.hpp> #include <boost/iostreams/stream.hpp> using boost::iostreams::stream; using boost::iostreams::file_descriptor_sink; using boost::filesystem::path; using boost::filesystem::exists; using boost::filesystem::status; using boost::filesystem::remove; int main(int argc, const char *argv[]) { char tmpTemplate[13]; strncpy(tmpTemplate, "/tmp/XXXXXX", 13); stream<file_descriptor_sink> tmp(mkstemp(tmpTemplate)); assert(tmp.is_open()); tmp << "Hello mkstemp!" << std::endl; tmp.close(); path tmpPath(tmpTemplate); if (exists(status(tmpPath))) { std::cout << "Output is in " << tmpPath.file_string() << std::endl; std::string cmd("cat "); cmd += tmpPath.file_string(); system(cmd.c_str()); std::cout << "Removing " << tmpPath.file_string() << std::endl; remove(tmpPath); } }


Probé la solución propuesta anteriormente para libstdc ++ por Piotr Dobrogost, y descubrí que tenía un defecto doloroso: debido a la falta de un constructor de movimiento apropiado para istream, es muy difícil sacar el objeto istream recién construido de la función creadora. . Otro problema con esto es que filtra un objeto FILE (aunque no es el descriptor subyacente del archivo POSIX). Aquí hay una solución alternativa que evita estos problemas:

#include <fstream> #include <string> #include <ext/stdio_filebuf.h> #include <type_traits> bool OpenFileForSequentialInput(ifstream& ifs, const string& fname) { ifs.open(fname.c_str(), ios::in); if (! ifs.is_open()) { return false; } using FilebufType = __gnu_cxx::stdio_filebuf<std::ifstream::char_type>; static_assert( std::is_base_of<ifstream::__filebuf_type, FilebufType>::value && (sizeof(FilebufType) == sizeof(ifstream::__filebuf_type)), "The filebuf type appears to have extra data members, the cast might be unsafe"); const int fd = static_cast<FilebufType*>(ifs.rdbuf())->fd(); assert(fd >= 0); if (0 != posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)) { ifs.close(); return false; } return true; }

La llamada a posix_fadvise () demuestra un uso potencial. También tenga en cuenta que el ejemplo utiliza static_assert y con el cual son C ++ 11, aparte de que debe construir bien en el modo C ++ 03.


Según entiendo, no existe una asociación con los punteros FILE o los descriptores de archivos en el modelo de objetos C ++ iostream para mantener el código portátil.

Dicho esto, vi que varios lugares se refieren a mds-utils o boost para ayudar a cerrar esa brecha.