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
C ++ 11

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");
C ++ 11

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 och out

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.

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

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();
C ++ 17

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 : returnerar true 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).



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow