Sök…
Introduktion
C ++ -fil I / O görs via strömmar . De viktigaste abstraktionerna är:
std::istream
för att läsa text.
std::ostream
för att skriva text.
std::streambuf
för att läsa eller skriva tecken.
Formaterad ingång använder operator>>
.
Formaterad utgång använder operator<<
.
Strömmar använder std::locale
, t.ex. för detaljer om formateringen och för översättning mellan externa kodningar och den interna kodningen.
Mer om strömmar: <iostream> Bibliotek
Öppnar en fil
Öppna en fil görs på samma sätt för alla 3 ifstream
( ifstream
, ofstream
och fstream
).
Du kan öppna filen direkt i konstruktören:
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.
Alternativt kan du använda filströmmens medlemsfunktion 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.
Du bör alltid kontrollera om en fil har öppnats framgångsrikt (även när du skriver). Fel kan innehålla: filen finns inte, filen har inte rätt åtkomsträttigheter, filen är redan i bruk, diskfel har inträffat, enheten har kopplats från ... Kontrollen kan göras enligt följande:
// 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");
}
När filsökvägen innehåller backstryk (till exempel på Windows-system) bör du undvika dem ordentligt:
// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes
eller använd rå literal:
// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal
eller använd snedstreck i stället:
// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:/folder/foo.txt");
Om du vill öppna fil med icke-ASCII-tecken i sökvägen för Windows för närvarande kan du använda icke-standardbrett teckenvägsargument:
// Open the file 'пример\foo.txt' on Windows.
std::ifstream ifs(LR"(пример\foo.txt)"); // using wide characters with raw literal
Läser från en fil
Det finns flera sätt att läsa data från en fil.
Om du vet hur informationen är formaterad kan du använda strömavsugningsoperatören ( >>
). Låt oss anta att du har en fil med namnet foo.txt som innehåller följande data:
John Doe 25 4 6 1987
Jane Doe 15 5 24 1976
Sedan kan du använda följande kod för att läsa informationen från filen:
// 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.
Operatören för >>
extraherar varje tecken och stoppar om den hittar ett tecken som inte kan lagras eller om det är ett specialtecken:
- För strängtyper stannar operatören vid ett vitrum (
) eller på en ny linje (
\n
). - För siffror stannar operatören vid ett icke-siffratecken.
Detta betyder att följande version av filen foo.txt också kommer att läsas av den föregående koden:
John
Doe 25
4 6 1987
Jane
Doe
15 5
24
1976
Operatören för >>
returnerar alltid den ström som ges till den. Därför kan flera operatörer kedjas ihop för att kunna läsa data i följd. En ström kan emellertid också användas som ett booleskt uttryck (som visas i while
slingan i föregående kod). Detta beror på att strömklasserna har en konverteringsoperatör för typen bool
. Denna bool()
-operatör kommer att returnera true
så länge som strömmen inte har några fel. Om en ström går in i ett feltillstånd (till exempel eftersom det inte kan extraheras mer data bool()
kommer operatören bool()
att returnera false
. Därför kommer while
loopen i den föregående koden att avslutas efter att inmatningsfilen har lästs till dess slut.
Om du vill läsa en hel fil som en sträng kan du använda följande kod:
// 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>());
Den här koden reserverar utrymme för string
för att minska onödiga minnesallokeringar.
Om du vill läsa en fil rad för rad kan du använda funktionen 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.
}
Om du vill läsa ett fast antal tecken kan du använda streamens medlemsfunktion read()
:
std::ifstream is("foo.txt");
char str[4];
// Read 4 characters from the file.
is.read(str, 4);
När du har utfört ett lästa kommando bör du alltid kontrollera om failbit
har ställts in, eftersom det indikerar om åtgärden misslyckades eller inte. Detta kan göras genom att anropa filströmmens medlemsfunktion fail()
:
is.read(str, 4); // This operation might fail for any reason.
if (is.fail())
// Failed to read!
Skriva till en fil
Det finns flera sätt att skriva till en fil. Det enklaste sättet är att använda en ofstream
( ofstream
) tillsammans med ströminföringsoperatören ( <<
):
std::ofstream os("foo.txt");
if(os.is_open()){
os << "Hello World!";
}
Istället för <<
kan du också använda utgångsfilströmmens medlemsfunktionsskrivning write()
:
std::ofstream os("foo.txt");
if(os.is_open()){
char data[] = "Foo";
// Writes 3 characters from data -> "Foo".
os.write(data, 3);
}
Efter att ha skrivit till en bäck, bör du alltid kontrollera om fel statligt sjunker badbit
har satts, eftersom det indikerar om operationen misslyckades eller inte. Detta kan göras genom att kalla utmatningsfilströmmen för medlemsfunktionen bad()
:
os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())
// Failed to write!
Öppningslägen
När du skapar en filström kan du ange ett öppningsläge. Ett öppningsläge är i princip en inställning för att kontrollera hur strömmen öppnar filen.
(Alla lägen finns i std::ios
namnutrymme.)
Ett öppningsläge kan tillhandahållas som en andra parameter till konstruktören för en filström eller till dess open()
-medlemfunktion:
std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);
std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);
Det bör noteras att du måste ställa in ios::in
eller ios::out
om du vill ställa in andra flaggor eftersom de inte implicit ställs in av iostreammedlemmarna även om de har ett korrekt standardvärde.
Om du inte anger ett öppningsläge används följande standardlägen:
-
ifstream
-in
-
ofstream
-out
-
fstream
-in
ochout
Filöppningslägen som du kan ange efter design är:
Läge | Menande | För | Beskrivning |
---|---|---|---|
app | bifoga | Produktion | Läggar till data i slutet av filen. |
binary | binär | Ingång / utgång | Inmatning och utgång sker binärt. |
in | inmatning | Inmatning | Öppnar filen för läsning. |
out | produktion | Produktion | Öppnar filen för skrivning. |
trunc | stympa | Ingång / utgång | Tar bort innehållet i filen när den öppnas. |
ate | i slutet | Inmatning | Går till slutet av filen när den öppnas. |
Obs: Om du ställer in det binary
läget kan data läsas / skrivas exakt som de är; genom att inte ställa in det möjliggör översättning av den nya linjen '\n'
tecken till / från en plattformsspecifik slutsekvens.
Till exempel i Windows är slutet av radsekvensen CRLF ( "\r\n"
).
Skriv: "\n"
=> "\r\n"
Läs: "\r\n"
=> "\n"
Stänger en fil
Det är sällan nödvändigt att stänga en fil i C ++, eftersom en filström automatiskt stänger den tillhörande filen i sin förstörare. Du bör dock försöka begränsa livslängden för ett filströmobjekt, så att det inte håller filhandtaget öppet längre än nödvändigt. Till exempel kan detta göras genom att sätta alla filoperationer i ett eget omfång ( {}
):
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.
Att ringa close()
uttryckligen nödvändigt om du vill återanvända samma fstream
objekt senare, men inte vill hålla filen öppen däremellan:
// 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();
Spola en ström
Filströmmar buffras som standard, liksom många andra typer av strömmar. Detta betyder att skrivningar till strömmen kanske inte får den underliggande filen att ändras omedelbart. Om du vill tvinga alla buffrade skrivningar att ske omedelbart kan du spola strömmen. Du kan göra detta antingen direkt genom att åberopa flush()
-metoden eller genom std::flush
stream manipulator:
std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;
char data[3] = "Foo";
os.write(data, 3);
os.flush();
Det finns en stream manipulator std::endl
som kombinerar att skriva en ny linje med spolning av strömmen:
// Both following lines do the same thing
os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;
Buffring kan förbättra prestandan att skriva till en ström. Därför bör applikationer som skriver mycket skriva undvika spolning i onödan. I motsats, om I / O görs sällan, bör applikationer överväga att spola ofta för att undvika att data fastnar i strömobjektet.
Läser en ASCII-fil i en std :: -sträng
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()`
}
rdbuf()
returnerar en pekare till en streambuf
som kan skjutas in i buffer
via stringstream::operator<<
medlemsfunktion.
En annan möjlighet (populariserad i Effektiv STL av Scott Meyers ) är:
std::ifstream f("file.txt");
if (f)
{
std::string str((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
// Operations on `str`...
}
Det här är trevligt eftersom det kräver lite kod (och gör det möjligt att läsa en fil direkt i vilken STL-behållare som helst, inte bara strängar) utan kan vara långsam för stora filer.
OBS : de extra parenteserna runt det första argumentet till strängkonstruktören är viktiga för att förhindra det mest irriterande parse- problemet.
Sist men inte minst:
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`...
}
vilket förmodligen är det snabbaste alternativet (bland de tre föreslagna).
Läser en fil i en behållare
I exemplet nedan använder vi std::string
och operator>>
att läsa objekt från filen.
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);
}
I exemplet ovan uppdateras vi helt enkelt genom filen och läser ett "objekt" åt gången med operator>>
. Samma påverkan kan uppnås med std::istream_iterator
som är en input-iterator som läser ett "objekt" åt gången från strömmen. Även de flesta containrar kan konstrueras med två iteratorer så vi kan förenkla koden ovan till:
std::ifstream file("file3.txt");
std::vector<std::string> v(std::istream_iterator<std::string>{file},
std::istream_iterator<std::string>{});
Vi kan utöka detta till att läsa alla objekttyper som vi gillar genom att helt enkelt ange det objekt vi vill läsa som mallparameter till std::istream_iterator
. Således kan vi helt enkelt utvidga ovanstående till att läsa rader (snarare än ord) som detta:
// 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>{});
Läser en "struktur" från en formaterad textfil.
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
Produktion:
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
Kopierar en fil
std::ifstream src("source_filename", std::ios::binary);
std::ofstream dst("dest_filename", std::ios::binary);
dst << src.rdbuf();
Med C ++ 17 är det vanliga sättet att kopiera en fil inklusive <filesystem>
-huvudet och använda copy_file
:
std::fileystem::copy_file("source_filename", "dest_filename");
Filsystembiblioteket utvecklades ursprungligen som boost.filesystem
och slogs slutligen till ISO C ++ från och med C ++ 17.
Kontrollera filens slut i ett slingkondition, dålig praxis?
eof
returnerar true
först efter att ha läst filens slut. Det indikerar INTE att nästa läsning kommer att vara slutet på strömmen.
while (!f.eof())
{
// Everything is OK
f >> buffer;
// What if *only* now the eof / fail bit is set?
/* Use `buffer` */
}
Du kan skriva korrekt:
while (!f.eof())
{
f >> buffer >> std::ws;
if (f.fail())
break;
/* Use `buffer` */
}
men
while (f >> buffer)
{
/* Use `buffer` */
}
är enklare och mindre fel benägna.
Ytterligare referenser:
-
std::ws
: kastar ledande blanksteg från en ingångsström -
std::basic_ios::fail
: returnerartrue
om ett fel har inträffat i den tillhörande strömmen
Skriva filer med icke-standardiserade språkinställningar
Om du behöver skriva en fil med olika språkinställningar till standard kan du använda std::locale
och std::basic_ios::imbue()
att göra det för en specifik filström:
Vägledning för användning:
- Du bör alltid använda en lokal på en ström innan du öppnar filen.
- När strömmen har satts in ska du inte ändra språk.
Anledningar till begränsningar: Att skapa en filström med ett språk har odefinierat beteende om det aktuella språket inte är statligt oberoende eller inte pekar i början av filen.
UTF-8-strömmar (och andra) är inte statliga oberoende. En filström med ett UTF-8-språk kan också läsa BOM-markören från filen när den öppnas. så att bara öppna filen kan läsa tecken från filen och den kommer inte att vara i början.
#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;
}
Att växla exakt till det klassiska C-språket är användbart om ditt program använder en annan standardinställning och du vill säkerställa en fast standard för att läsa och skriva filer. Med ett "C" -föredraget språk, skriver exemplet
78,123.456
78,123.456
78123.456
Om till exempel det föredragna språket är tyska och därmed använder ett annat nummerformat, skriver exemplet
78 123,456
78,123.456
78123.456
(notera decimal komma i den första raden).