Suche…


Einführung

C ++ - Datei-E / A erfolgt über Streams . Die wichtigsten Abstraktionen sind:

std::istream zum Lesen von Text.

std::ostream zum Schreiben von Text.

std::streambuf zum Lesen oder Schreiben von Zeichen.

Formatierte Eingabe verwendet operator>> .

Die formatierte Ausgabe verwendet den operator<< .

Streams verwenden std::locale , z. B. für Details zur Formatierung und für die Übersetzung zwischen externen Kodierungen und der internen Kodierung.

Weitere Informationen zu Streams: <iostream> Bibliothek

Datei öffnen

Das Öffnen einer Datei erfolgt für alle 3 Dateistreams ( ifstream , ofstream und fstream ) auf dieselbe Weise.

Sie können die Datei direkt im Konstruktor öffnen:

std::ifstream ifs("foo.txt");  // ifstream: Opens file "foo.txt" for reading only.

std::ofstream ofs("foo.txt");  // ofstream: Opens file "foo.txt" for writing only.

std::fstream iofs("foo.txt");  // fstream:  Opens file "foo.txt" for reading and writing.

Alternativ können Sie die Member-Funktion des Dateistreams open() :

std::ifstream ifs;
ifs.open("bar.txt");           // ifstream: Opens file "bar.txt" for reading only.

std::ofstream ofs;
ofs.open("bar.txt");           // ofstream: Opens file "bar.txt" for writing only.

std::fstream iofs;
iofs.open("bar.txt");          // fstream:  Opens file "bar.txt" for reading and writing.

Sie sollten immer überprüfen, ob eine Datei erfolgreich geöffnet wurde (auch beim Schreiben). Zu den Fehlern können gehören: Die Datei ist nicht vorhanden, die Datei hat nicht die richtigen Zugriffsrechte, die Datei wird bereits verwendet, Datenträgerfehler sind aufgetreten, das Laufwerk ist nicht verbunden. Die Überprüfung kann wie folgt durchgeführt werden:

// Try to read the file 'foo.txt'.
std::ifstream ifs("fooo.txt");  // Note the typo; the file can't be opened.

// Check if the file has been opened successfully.
if (!ifs.is_open()) {
    // The file hasn't been opened; take appropriate actions here.
    throw CustomException(ifs, "File could not be opened");
}

Wenn der Dateipfad umgekehrte Schrägstriche enthält (z. B. auf einem Windows-System), sollten Sie diese ordnungsgemäß deaktivieren:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes
C ++ 11

oder verwenden Sie Rohliteral:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal

oder verwenden Sie stattdessen Schrägstriche:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:/folder/foo.txt");
C ++ 11

Wenn Sie unter Windows eine Datei mit Nicht-ASCII-Zeichen im Pfad öffnen möchten, können Sie ein nicht standardmäßiges Zeichenpfadargument verwenden:

// Open the file 'пример\foo.txt' on Windows.
std::ifstream ifs(LR"(пример\foo.txt)"); // using wide characters with raw literal

Lesen aus einer Datei

Es gibt verschiedene Möglichkeiten, Daten aus einer Datei zu lesen.

Wenn Sie wissen, wie die Daten formatiert sind, können Sie den Stream-Extraktionsoperator ( >> ) verwenden. Nehmen wir an, Sie haben eine Datei namens foo.txt, die die folgenden Daten enthält:

John Doe 25 4 6 1987
Jane Doe 15 5 24 1976

Dann können Sie den folgenden Code verwenden, um diese Daten aus der Datei zu lesen:

// Define variables.
std::ifstream is("foo.txt");
std::string firstname, lastname;
int age, bmonth, bday, byear;

// Extract firstname, lastname, age, bday month, bday day, and bday year in that order.
// Note: '>>' returns false if it reached EOF (end of file) or if the input data doesn't
// correspond to the type of the input variable (for example, the string "foo" can't be
// extracted into an 'int' variable).
while (is >> firstname >> lastname >> age >> bmonth >> bday >> byear)
    // Process the data that has been read.

Der Stream-Extraktionsoperator >> extrahiert jedes Zeichen und stoppt, wenn ein Zeichen gefunden wird, das nicht gespeichert werden kann oder wenn es ein Sonderzeichen ist:

  • Bei Zeichenfolgentypen stoppt der Operator bei einem Leerzeichen ( ) oder bei einem Newline ( \n ).
  • Bei Nummern stoppt der Operator bei einem Nicht-Zeichen.

Das bedeutet, dass die folgende Version der Datei foo.txt auch vom vorherigen Code erfolgreich gelesen werden kann:

John 
Doe 25
4 6 1987


Jane
Doe 
15 5
24
1976

Der Stream-Operator >> immer den Strom zu ihm gegeben. Daher können mehrere Operatoren miteinander verkettet werden, um Daten nacheinander lesen zu können. Ein Stream kann jedoch auch als boolescher Ausdruck verwendet werden (wie im vorherigen Code in der while Schleife gezeigt). Dies liegt daran, dass die Stream-Klassen einen Konvertierungsoperator für den Typ bool . Dieser bool() Operator gibt true , solange der Stream keine Fehler enthält. Wenn ein Stream in einen Fehlerzustand wechselt (z. B. weil keine Daten mehr extrahiert werden können), gibt der bool() Operator den bool() false . Daher wird die while Schleife im vorherigen Code beendet, nachdem die Eingabedatei bis zum Ende gelesen wurde.

Wenn Sie eine gesamte Datei als Zeichenfolge lesen möchten, können Sie den folgenden Code verwenden:

// Opens 'foo.txt'.
std::ifstream is("foo.txt");
std::string whole_file;

// Sets position to the end of the file.
is.seekg(0, std::ios::end);

// Reserves memory for the file.
whole_file.reserve(is.tellg());

// Sets position to the start of the file.
is.seekg(0, std::ios::beg);

// Sets contents of 'whole_file' to all characters in the file.
whole_file.assign(std::istreambuf_iterator<char>(is),
  std::istreambuf_iterator<char>());

Dieser Code reserviert Speicherplatz für die string um nicht benötigte Speicherzuordnungen zu reduzieren.

Wenn Sie eine Datei Zeile für Zeile lesen möchten, können Sie die Funktion getline() :

std::ifstream is("foo.txt");   

// The function getline returns false if there are no more lines.
for (std::string str; std::getline(is, str);) {
    // Process the line that has been read.
}

Wenn Sie eine feste Anzahl von Zeichen lesen möchten, können Sie die Member-Funktion read() des Streams verwenden:

std::ifstream is("foo.txt");
char str[4];

// Read 4 characters from the file.
is.read(str, 4);

Nach der Ausführung eines failbit sollten Sie immer überprüfen, ob das Fehlerstatusflag- failbit gesetzt ist, da es anzeigt, ob die Operation fehlgeschlagen ist oder nicht. Dies kann durch Aufrufen der Member-Funktion fail() des Dateistreams erfolgen:

is.read(str, 4); // This operation might fail for any reason.

if (is.fail())
    // Failed to read!

In eine Datei schreiben

Es gibt verschiedene Möglichkeiten, in eine Datei zu schreiben. Am einfachsten ist es, einen Ausgabedatei-Stream ( ofstream ) zusammen mit dem Stream- ofstream ( << ) zu verwenden:

std::ofstream os("foo.txt");
if(os.is_open()){
    os << "Hello World!";
}

Anstelle von << können Sie auch die Member-Funktion write() der Ausgabedatei verwenden:

std::ofstream os("foo.txt");
if(os.is_open()){
    char data[] = "Foo";

    // Writes 3 characters from data -> "Foo".
    os.write(data, 3);
}

Nach dem Schreiben in einen Stream sollten Sie immer überprüfen, ob das Fehlerstatusflag badbit gesetzt wurde, da es anzeigt, ob die Operation fehlgeschlagen ist oder nicht. Dies kann durch Aufrufen der Member-Funktion bad() des Ausgabedatei-Streams erfolgen:

os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())
    // Failed to write!

Öffnungsmodi

Beim Erstellen eines Dateistreams können Sie einen Öffnungsmodus angeben. Ein Öffnungsmodus ist im Wesentlichen eine Einstellung, um zu steuern, wie der Stream die Datei öffnet.

(Alle Modi befinden sich im std::ios Namespace.)

Ein Öffnungsmodus kann dem Konstruktor eines Dateistreams oder seiner open() - Memberfunktion als zweiter Parameter bereitgestellt werden:

std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);

std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);

Es ist zu beachten, dass Sie ios::in oder ios::out wenn Sie andere Flags setzen möchten, da sie von den iostream-Mitgliedern nicht implizit gesetzt werden, obwohl sie einen korrekten Standardwert haben.

Wenn Sie keinen Öffnungsmodus angeben, werden die folgenden Standardmodi verwendet:

  • ifstream - in
  • ofstream - out
  • fstream - in und out

Die Dateiöffnungsmodi, die Sie von Entwurf festlegen können, sind

Modus Bedeutung Zum Beschreibung
app anhängen Ausgabe Hängt Daten am Ende der Datei an.
binary binär Input-Output Ein- und Ausgabe erfolgen binär.
in Eingang Eingang Öffnet die Datei zum Lesen.
out Ausgabe Ausgabe Öffnet die Datei zum Schreiben.
trunc kürzen Input-Output Entfernt den Inhalt der Datei beim Öffnen.
ate am ende Eingang Geht beim Öffnen an das Ende der Datei.

Hinweis: Durch das Einstellen des binary können die Daten genau wie sie sind gelesen / geschrieben werden. Wenn Sie diese Einstellung nicht festlegen '\n' das Newline '\n' Zeichen '\n' in eine plattformspezifische Zeilenende-Sequenz übersetzt.

Unter Windows lautet die Zeilenende-Sequenz beispielsweise CRLF ( "\r\n" ).
Schreiben Sie: "\n" => "\r\n"
Lesen Sie: "\r\n" => "\n"

Eine Datei schließen

Das explizite Schließen einer Datei ist in C ++ selten erforderlich, da ein Dateistream automatisch die zugehörige Datei in seinem Destruktor schließt. Sie sollten jedoch versuchen, die Lebensdauer eines Dateistream-Objekts zu begrenzen, damit der Dateikennungscode nicht länger als erforderlich geöffnet bleibt. Dies kann beispielsweise geschehen, indem alle Dateioperationen in einen eigenen Bereich ( {} ) gestellt werden:

std::string const prepared_data = prepare_data();
{
    // Open a file for writing.
    std::ofstream output("foo.txt");

    // Write data.
    output << prepared_data;
}  // The ofstream will go out of scope here.
   // Its destructor will take care of closing the file properly.

Das explizite Aufrufen von close() ist nur erforderlich, wenn Sie dasselbe fstream Objekt später erneut verwenden möchten, die Datei jedoch nicht geöffnet bleiben soll:

// Open the file "foo.txt" for the first time.
std::ofstream output("foo.txt");

// Get some data to write from somewhere.
std::string const prepared_data = prepare_data();

// Write data to the file "foo.txt".
output << prepared_data;

// Close the file "foo.txt".
output.close();

// Preparing data might take a long time. Therefore, we don't open the output file stream
// before we actually can write some data to it.
std::string const more_prepared_data = prepare_complex_data();

// Open the file "foo.txt" for the second time once we are ready for writing.
output.open("foo.txt");

// Write the data to the file "foo.txt".
output << more_prepared_data;

// Close the file "foo.txt" once again.
output.close();

Einen Strom spülen

Dateistreams werden wie viele andere Streamtypen standardmäßig gepuffert. Dies bedeutet, dass Schreibvorgänge in den Stream möglicherweise nicht dazu führen, dass die zugrunde liegende Datei sofort geändert wird. Um zu erzwingen, dass alle gepufferten Schreibvorgänge sofort ausgeführt werden, können Sie den Stream leeren . Sie können dies entweder direkt durch Aufrufen der Methode flush() oder über den Stream-Manipulator std::flush tun:

std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;

char data[3] = "Foo";
os.write(data, 3);
os.flush();

Es gibt einen Stream-Manipulator std::endl , der das Schreiben einer neuen Zeile mit dem Leeren des Streams kombiniert:

// Both following lines do the same thing
os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;

Durch Pufferung kann die Leistung beim Schreiben in einen Stream verbessert werden. Daher sollten Anwendungen, die viel schreiben, das Flushing nicht unnötig vermeiden. Im Gegensatz dazu sollten Anwendungen bei häufigem E / A-Vorgang das häufige Leeren in Betracht ziehen, um zu verhindern, dass Daten im Stream-Objekt hängen bleiben.

Lesen einer ASCII-Datei in einen std :: string

std::ifstream f("file.txt");

if (f)
{
  std::stringstream buffer;
  buffer << f.rdbuf();
  f.close();

  // The content of "file.txt" is available in the string `buffer.str()`
}

Die rdbuf() -Methode gibt einen Zeiger auf eine streambuf , der über den stringstream::operator<< member stringstream::operator<< in den buffer stringstream::operator<< kann.


Eine andere Möglichkeit (populär in Effective STL von Scott Meyers ) ist:

std::ifstream f("file.txt");

if (f)
{
  std::string str((std::istreambuf_iterator<char>(f)),
                  std::istreambuf_iterator<char>());

  // Operations on `str`...
}

Dies ist schön, da nur wenig Code erforderlich ist (und das direkte Lesen von Dateien in STL-Container und nicht nur in Strings möglich ist). Bei großen Dateien kann dies jedoch langsam sein.

ANMERKUNG : Die zusätzlichen Klammern um das erste Argument des String-Konstruktors sind wichtig, um das ärgerliche Analyseproblem zu vermeiden.


Zu guter Letzt:

std::ifstream f("file.txt");

if (f)
{
  f.seekg(0, std::ios::end);
  const auto size = f.tellg();

  std::string str(size, ' ');
  f.seekg(0);
  f.read(&str[0], size); 
  f.close();

  // Operations on `str`...
}

Dies ist wahrscheinlich die schnellste Option (unter den drei vorgeschlagenen).

Eine Datei in einen Container lesen

Im folgenden Beispiel verwenden wir std::string und operator>> , um Elemente aus der Datei zu lesen.

    std::ifstream file("file3.txt");

    std::vector<std::string>  v;

    std::string s;
    while(file >> s) // keep reading until we run out
    {
        v.push_back(s);
    }

Im obigen Beispiel durchlaufen wir einfach die Datei und lesen jeweils einen "Eintrag" mit operator>> . Der gleiche std::istream_iterator kann mit dem std::istream_iterator erzielt werden, einem Eingabe-Iterator, der jeweils ein "Element" aus dem Stream liest. Die meisten Container können auch mit zwei Iteratoren erstellt werden, um den obigen Code zu vereinfachen:

    std::ifstream file("file3.txt");

    std::vector<std::string>  v(std::istream_iterator<std::string>{file},
                                std::istream_iterator<std::string>{});

Wir können dies erweitern, um beliebige Objekttypen zu lesen, indem wir einfach das Objekt std::istream_iterator , das wir als Vorlagenparameter für std::istream_iterator lesen std::istream_iterator . Daher können wir das Obige einfach erweitern, um Zeilen (anstatt Wörter) wie folgt zu lesen:

// Unfortunately there is  no built in type that reads line using >>
// So here we build a simple helper class to do it. That will convert
// back to a string when used in string context.
struct Line
{
    // Store data here
    std::string data;
    // Convert object to string
    operator std::string const&() const {return data;}
    // Read a line from a stream.
    friend std::istream& operator>>(std::istream& stream, Line& line)
    {
        return std::getline(stream, line.data);
    }
};


    std::ifstream file("file3.txt");

    // Read the lines of a file into a container.
    std::vector<std::string>  v(std::istream_iterator<Line>{file},
                                std::istream_iterator<Line>{});

Lesen einer `struct` aus einer formatierten Textdatei.

C ++ 11
struct info_type
{
    std::string name;
    int age;
    float height;
    
    // we define an overload of operator>> as a friend function which
    // gives in privileged access to private data members 
    friend std::istream& operator>>(std::istream& is, info_type& info)
    {
        // skip whitespace
        is >> std::ws;
        std::getline(is, info.name);
        is >> info.age;
        is >> info.height;
        return is;
    }
};

void func4()
{
    auto file = std::ifstream("file4.txt");

    std::vector<info_type> v;

    for(info_type info; file >> info;) // keep reading until we run out
    {
        // we only get here if the read succeeded
        v.push_back(info);
    }

    for(auto const& info: v)
    {
        std::cout << "  name: " << info.name << '\n';
        std::cout << "   age: " << info.age << " years" << '\n';
        std::cout << "height: " << info.height << "lbs" << '\n';
        std::cout << '\n';
    }
}

file4.txt

Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8

Ausgabe:

name: Wogger Wabbit
 age: 2 years
height: 6.2lbs

name: Bilbo Baggins
 age: 111 years
height: 81.3lbs

name: Mary Poppins
 age: 29 years
height: 154.8lbs

Datei kopieren

std::ifstream  src("source_filename", std::ios::binary);
std::ofstream  dst("dest_filename",   std::ios::binary);
dst << src.rdbuf();
C ++ 17

Mit C ++ 17 können Sie eine Datei standardmäßig mit dem Header <filesystem> und mit copy_file :

std::fileystem::copy_file("source_filename", "dest_filename");

Die Dateisystembibliothek wurde ursprünglich als boost.filesystem und ab C ++ 17 mit ISO C ++ zusammengeführt.

Dateiende innerhalb einer Schleifenbedingung prüfen, schlechte Praxis?

eof gibt true erst nach dem Ende der Datei zu lesen. Es bedeutet NICHT, dass der nächste Lesevorgang das Ende des Streams ist.

while (!f.eof())
{
  // Everything is OK

  f >> buffer;

  // What if *only* now the eof / fail bit is set?

  /* Use `buffer` */
}

Sie könnten richtig schreiben:

while (!f.eof()) 
{  
  f >> buffer >> std::ws;

  if (f.fail())
    break;

  /* Use `buffer` */
}

aber

while (f >> buffer)
{
  /* Use `buffer` */
}

ist einfacher und weniger fehleranfällig.

Weitere referenzen:

Schreiben von Dateien mit nicht standardmäßigen Gebietsschemaeinstellungen

Wenn Sie eine Datei mit anderen Ländereinstellungen als Standard schreiben müssen, können Sie std::locale und std::basic_ios::imbue() um dies für einen bestimmten Dateistream zu tun:

Anleitung zur Verwendung:

  • Sie sollten immer einen lokalen Wert auf einen Stream anwenden, bevor Sie die Datei öffnen.
  • Sobald der Stream durchdrungen ist, sollten Sie das Gebietsschema nicht ändern.

Gründe für Einschränkungen: Das Erstellen eines Dateistreams mit einem Gebietsschema hat ein undefiniertes Verhalten, wenn das aktuelle Gebietsschema nicht zustandsunabhängig ist oder nicht auf den Anfang der Datei zeigt.

UTF-8-Streams (und andere) sind nicht zustandsunabhängig. Ein Dateistream mit einem UTF-8-Gebietsschema kann auch versuchen, die Stücklistenmarkierung aus der Datei zu lesen, wenn sie geöffnet wird. Wenn Sie also die Datei öffnen, werden möglicherweise Zeichen aus der Datei gelesen, und es befindet sich nicht am Anfang.

#include <iostream>
#include <fstream>
#include <locale>

int main()
{
  std::cout << "User-preferred locale setting is "
            << std::locale("").name().c_str() << std::endl;

  // Write a floating-point value using the user's preferred locale.
  std::ofstream ofs1;
  ofs1.imbue(std::locale(""));
  ofs1.open("file1.txt");
  ofs1 << 78123.456 << std::endl;

  // Use a specific locale (names are system-dependent)
  std::ofstream ofs2;
  ofs2.imbue(std::locale("en_US.UTF-8"));
  ofs2.open("file2.txt");
  ofs2 << 78123.456 << std::endl;

  // Switch to the classic "C" locale
  std::ofstream ofs3;
  ofs3.imbue(std::locale::classic());
  ofs3.open("file3.txt");
  ofs3 << 78123.456 << std::endl;
}

Das explizite Umschalten auf das klassische Gebietsschema "C" ist hilfreich, wenn Ihr Programm ein anderes Standardgebietsschema verwendet und Sie einen festen Standard für das Lesen und Schreiben von Dateien sicherstellen möchten. Bei einem bevorzugten Gebietsschema "C" schreibt das Beispiel

78,123.456
78,123.456
78123.456

Wenn das bevorzugte Gebietsschema beispielsweise Deutsch ist und daher ein anderes Zahlenformat verwendet, schreibt das Beispiel

78 123,456
78,123.456
78123.456

(Beachten Sie das Komma in der ersten Zeile).



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow