Wie konstruiere ich einen C++-Fstream aus einem POSIX-Dateideskriptor?

Lesezeit: 8 Minuten

Wie konstruiere ich einen C Fstream aus einem POSIX Dateideskriptor
BD in Rivenhill

Ich suche im Grunde nach einer C++-Version von fdopen(). Ich habe ein wenig darüber recherchiert und es ist eines dieser Dinge, die so aussehen, als sollten sie einfach sein, sich aber als sehr kompliziert herausstellen. Übersehe ich etwas in diesem Glauben (dh es ist wirklich einfach)? Wenn nicht, gibt es irgendwo eine gute Bibliothek, um damit umzugehen?

BEARBEITEN: Meine Beispiellösung wurde in eine separate Antwort verschoben.

  • Windows und Linux können das mmap in die Datei und stellte ihren Inhalt als Byte-Array bereit.

    – Eigenfeld

    21. April 2020 um 21:57 Uhr

Wie konstruiere ich einen C Fstream aus einem POSIX Dateideskriptor
Piotr Dobrogost

Aus der Antwort von Éric Malenfant:

AFAIK, es gibt keine Möglichkeit, dies in Standard-C++ zu tun. Abhängig von Ihrer Plattform kann Ihre Implementierung der Standardbibliothek (als nicht standardmäßige Erweiterung) einen fstream-Konstruktor anbieten, der einen Dateideskriptor als Eingabe verwendet. (Dies ist der Fall bei libstdc++, IIRC) oder einer FILE*.

Basierend auf den obigen Beobachtungen und meinen Recherchen unten gibt es funktionierenden Code in zwei Varianten; eine für libstdc++ und eine für Microsoft Visual C++.


libstdc++

Es gibt Nicht-Standard __gnu_cxx::stdio_filebuf Klassenvorlage, die erbt std::basic_streambuf und hat den folgenden Konstruktor

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

mit Beschreibung Dieser Konstruktor ordnet einen Dateistrompuffer einem offenen POSIX-Dateideskriptor zu.

Wir erstellen es, indem wir das POSIX-Handle (Zeile 1) übergeben, und übergeben es dann als basic_streambuf (Zeile 2) an den Konstruktor von istream:

#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++

Früher gab es Nicht-Standard Ausführung des Konstruktors von ifstream, der den POSIX-Dateideskriptor verwendet, aber es fehlen beide Strom docs und aus dem Code. Es gibt eine andere nicht standardmäßige Version des Konstruktors von ifstream, die FILE * verwendet

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

und es ist nicht dokumentiert (ich konnte nicht einmal eine alte Dokumentation finden, in der es vorhanden wäre). Wir rufen es auf (Zeile 1), wobei der Parameter das Ergebnis des Aufrufs ist _fdöffnen um C-Stream FILE* vom POSIX-Dateihandle zu erhalten.

#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;
}

  • Nun die akzeptierte Antwort der Vollständigkeit halber. Andere könnten an meiner Lösung mit Boost interessiert sein, die in eine separate Antwort verschoben wurde.

    – BD in Rivenhill

    25. Februar 2013 um 19:16 Uhr

  • Für Linux: Wenn Sie sich ios_init.cc in gcc ansehen (die Quelle, die ich habe, ist für Version 4.1.1), wird std::cout initialisiert, indem ein stdio_sync_filebuf um Ihren Dateideskriptor initialisiert wird, und dann auf ostream um Ihren stdio_sync_filebuf< initialisiert wird Zeichen>. Ich kann jedoch nicht behaupten, dass dies stabil sein wird.

    – Funkelnd

    26. Juni 2014 um 12:17 Uhr

  • @Sparky Nachsehen std::cout Umsetzung ist eine gute Idee. Ich frage mich, was der Unterschied zwischen ist stdio_filebuf und stdio_sync_filebuf?

    – Piotr Dobrogost

    27. April 2016 um 7:40 Uhr

  • POSIX fds in MSVC sind Emulation. Die Windows-API für Dateioperationen unterscheidet sich in vielerlei Hinsicht von der POSIX-API – verschiedene Funktionsnamen und Datentypen von Parametern. Windows verwendet intern sogenannte “Handles”, um verschiedene Windows-API-Objekte zu identifizieren, und der Windows-API-Typ HANDLE ist als void* definiert, so weiter Minimum passt es nicht in “int” (das 32-Bit ist) auf 64-Bit-Plattformen. Für Windows könnten Sie also daran interessiert sein, nach einem Stream zu suchen, der es ermöglicht, über die Windows-API-Datei HANDLE zu arbeiten.

    – ivan.ukr

    8. Januar 2019 um 7:58 Uhr


1646984412 876 Wie konstruiere ich einen C Fstream aus einem POSIX Dateideskriptor
Eric Malenfant

AFAIK, es gibt keine Möglichkeit, dies in Standard-C++ zu tun. Abhängig von Ihrer Plattform kann Ihre Implementierung der Standardbibliothek (als nicht standardmäßige Erweiterung) einen fstream-Konstruktor anbieten, der einen Dateideskriptor verwendet (dies ist der Fall für libstdc++, IIRC) oder a FILE* als Eingang.

Eine andere Alternative wäre die Verwendung von a boost::iostreams::file_descriptor Gerät, das Sie in ein wickeln könnten boost::iostreams::stream wenn Sie eine std::stream-Schnittstelle dazu haben möchten.

  • Angesichts der Tatsache, dass dies die einzige tragbare Lösung ist, verstehe ich nicht, warum dies nicht die akzeptierte oder am besten bewertete Antwort ist.

    – Martin

    12. März 2016 um 10:40 Uhr

  • Ich denke, diese Antwort zeigt ein Beispiel: stackoverflow.com/questions/1957761/…

    – Mark Lakata

    9. Februar um 0:30 Uhr

Es besteht eine gute Chance, dass Ihr Compiler einen FILE-basierten fstream-Konstruktor anbietet, obwohl er nicht dem Standard entspricht. Zum Beispiel:

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

Aber soweit ich weiß, gibt es keine tragbare Möglichkeit, dies zu tun.

  • Beachten Sie, dass g++ (korrekterweise) dies im c++11-Modus nicht zulässt

    – Mark K. Cowan

    18. August 2014 um 9:57 Uhr

Ein Teil der ursprünglichen (nicht angegebenen) Motivation dieser Frage besteht darin, Daten entweder zwischen Programmen oder zwischen zwei Teilen eines Testprogramms mithilfe einer sicher erstellten temporären Datei übertragen zu können, aber tmpnam() gibt eine Warnung in gcc aus, also wollte ich um stattdessen mkstemp() zu verwenden. Hier ist ein Testprogramm, das ich basierend auf der Antwort von Éric Malenfant geschrieben habe, aber mkstemp() anstelle von fdopen() verwende; Dies funktioniert auf meinem Ubuntu-System mit installierten Boost-Bibliotheken:

#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);
  }
}

1646984413 566 Wie konstruiere ich einen C Fstream aus einem POSIX Dateideskriptor
Kennzeichen

Es ist eigentlich ganz einfach. Nicolai M. Josuttis wurde entlassen fdstream in Verbindung mit seinem Buch Die C++-Standardbibliothek – ein Tutorial und eine Referenz. Sie finden die 184-Zeilen-Implementierung Hier.

  • Dies ist eine interessante Anwendung des Ausdrucks „ziemlich einfach“.

    – Markus Adler

    28. März 2021 um 3:25 Uhr

  • Es wird einfacher, wenn Sie die Boost-Klasse verwenden, die diesen Code enthält, wie in einer anderen Antwort vorgeschlagen.

    – Toby Speight

    27. Oktober 2021 um 15:27 Uhr

1646984413 564 Wie konstruiere ich einen C Fstream aus einem POSIX Dateideskriptor
Isac Casapu

Ich habe die oben vorgeschlagene Lösung für libstdc++ von Piotr Dobrogost ausprobiert und festgestellt, dass sie einen schmerzhaften Fehler hatte: Aufgrund des Fehlens eines geeigneten Bewegungskonstruktors für istream ist es sehr schwierig, das neu konstruierte istream-Objekt aus der Erstellungsfunktion herauszuholen . Ein weiteres Problem dabei ist, dass ein FILE-Objekt verloren geht (obwohl nicht der zugrunde liegende Posix-Dateideskriptor). Hier ist eine alternative Lösung, die diese Probleme vermeidet:

#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;
}

Der Aufruf von posix_fadvise() demonstriert eine mögliche Verwendung. Beachten Sie auch, dass das Beispiel verwendet static_assert und verwenden die C++ 11 sind, ansonsten sollte es im C++ 03-Modus gut gebaut werden.

  • Dies ist eine interessante Anwendung des Ausdrucks „ziemlich einfach“.

    – Markus Adler

    28. März 2021 um 3:25 Uhr

  • Es wird einfacher, wenn Sie die Boost-Klasse verwenden, die diesen Code enthält, wie in einer anderen Antwort vorgeschlagen.

    – Toby Speight

    27. Oktober 2021 um 15:27 Uhr

1646984414 314 Wie konstruiere ich einen C Fstream aus einem POSIX Dateideskriptor
Sockel

Mein Verständnis ist, dass es keine Zuordnung zu FILE-Zeigern oder Dateideskriptoren im C++-Iostream-Objektmodell gibt, um Code portabel zu halten.

Das heißt, ich habe mehrere Orte gesehen, auf die verwiesen wird mds-utils oder steigern, um diese Lücke zu schließen.

  • FILE* ist Standard-C und damit C++, daher sehe ich nicht, wie die Aktivierung von C++-Streams für die Arbeit mit C-Streams die Portabilität beeinträchtigen könnte

    – Piotr Dobrogost

    11. März 2011 um 20:48 Uhr

990230cookie-checkWie konstruiere ich einen C++-Fstream aus einem POSIX-Dateideskriptor?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy