C++
Datei I / O
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
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");
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
undout
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.
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();
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:
-
std::ws
: verwirft führende Leerzeichen aus einem Eingabestrom -
std::basic_ios::fail
:std::basic_ios::fail
true
zurücktrue
wenn im zugeordneten Stream ein Fehler aufgetreten ist
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).