Zoeken…


Invoering

C ++ file I / O wordt gedaan via streams . De belangrijkste abstracties zijn:

std::istream voor het lezen van tekst.

std::ostream voor het schrijven van tekst.

std::streambuf voor het lezen of schrijven van tekens.

Opgemaakte invoer gebruikt operator>> .

Opgemaakte uitvoer gebruikt operator<< .

Streams gebruiken std::locale , bijvoorbeeld voor details van de opmaak en voor vertaling tussen externe coderingen en de interne codering.

Meer over streams: <iostream> Bibliotheek

Een bestand openen

Het openen van een bestand gebeurt op dezelfde manier voor alle 3 bestandsstreams ( ifstream , ofstream en fstream ).

U kunt het bestand rechtstreeks in de constructor openen:

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.

U kunt ook de ledenfunctie van de bestandsstream 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.

U moet altijd controleren of een bestand met succes is geopend (zelfs tijdens het schrijven). Storingen kunnen zijn: het bestand bestaat niet, het bestand heeft niet de juiste toegangsrechten, het bestand is al in gebruik, er zijn schijffouten opgetreden, de schijf is losgekoppeld ... Controle kan als volgt worden uitgevoerd:

// 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");
}

Wanneer het bestandspad backslashes bevat (bijvoorbeeld op een Windows-systeem), moet u deze correct ontsnappen:

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

of gebruik onbewerkte letterlijke:

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

of gebruik in plaats daarvan schuine strepen:

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

Als u een bestand met niet-ASCII-tekens in het pad op Windows wilt openen, kunt u momenteel een niet-standaard breed karakter padargument gebruiken:

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

Uit een bestand lezen

Er zijn verschillende manieren om gegevens uit een bestand te lezen.

Als u weet hoe de gegevens worden opgemaakt, kunt u de streamextractie-operator gebruiken ( >> ). Laten we aannemen dat u een bestand met de naam foo.txt hebt dat de volgende gegevens bevat:

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

Vervolgens kunt u de volgende code gebruiken om die gegevens uit het bestand te lezen:

// 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.

De streamextractie-operator >> extraheert elk teken en stopt als het een teken vindt dat niet kan worden opgeslagen of als het een speciaal teken is:

  • Voor tekenreekstypes stopt de operator op een witruimte ( ) of op een nieuwe regel ( \n ).
  • Voor cijfers stopt de operator bij een niet-nummerteken.

Dit betekent dat de volgende versie van het bestand foo.txt ook met succes kan worden gelezen door de vorige code:

John 
Doe 25
4 6 1987


Jane
Doe 
15 5
24
1976

De streamextractie-operator >> retourneert altijd de stream die eraan is gegeven. Daarom kunnen meerdere operatoren aan elkaar worden gekoppeld om gegevens achter elkaar te lezen. Een stream kan echter ook worden gebruikt als een Booleaanse expressie (zoals weergegeven in de while lus in de vorige code). Dit komt omdat de streamklassen een conversie-operator hebben voor het type bool . Deze bool() -operator retourneert true zolang de stream geen fouten bevat. Als een stream een foutstatus krijgt (bijvoorbeeld omdat er geen gegevens meer kunnen worden geëxtraheerd), retourneert de operator bool() false . Daarom wordt de while lus in de vorige code verlaten nadat het invoerbestand volledig is gelezen.

Als u een heel bestand als een string wilt lezen, kunt u de volgende code gebruiken:

// 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>());

Deze code reserveert ruimte voor de string om onnodige geheugentoewijzingen te verminderen.

Als u een bestand regel voor regel wilt lezen, kunt u de functie 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.
}

Als je een vast aantal tekens wilt lezen, kun je de ledenfunctie van de stream read() :

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

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

Na het uitvoeren van een failbit moet u altijd controleren of de foutstatus vlag failbit is ingesteld, omdat deze aangeeft of de bewerking is mislukt of niet. Dit kan worden gedaan door de ledenfunctie van de bestandsstream te laten fail() :

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

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

Naar een bestand schrijven

Er zijn verschillende manieren om naar een bestand te schrijven. De eenvoudigste manier is om een uitvoerbestandstream ( ofstream ) te gebruiken samen met de streaminvoegoperator ( << ):

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

In plaats van << kunt u ook de ledenfunctie van de uitvoerbestandsstroom write() :

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

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

Nadat u naar een stream hebt geschreven, moet u altijd controleren of de badbit vlag is ingesteld, omdat deze aangeeft of de bewerking is mislukt. Dit kan worden gedaan door de lidfunctie van de uitvoerbestandsstroom bad() noemen:

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

Openingsmodi

Wanneer u een bestandsstream maakt, kunt u een openingsmodus opgeven. Een openingsmodus is in feite een instelling om te bepalen hoe de stream het bestand opent.

(Alle modi zijn te vinden in de std::ios naamruimte.)

Een openingsmodus kan worden verstrekt als tweede parameter voor de constructor van een bestandsstream of voor de open() lidfunctie:

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

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

Opgemerkt moet worden dat u ios::in of ios::out als u andere vlaggen wilt instellen omdat deze niet impliciet worden ingesteld door de iostream-leden, hoewel ze een juiste standaardwaarde hebben.

Als u geen openingsmodus opgeeft, worden de volgende standaardmodi gebruikt:

  • ifstream - in
  • ofstream - out
  • fstream - in en out

De bestandsopeningsmodi die u per ontwerp kunt opgeven, zijn:

mode Betekenis Voor Beschrijving
app append uitgang Voegt gegevens toe aan het einde van het bestand.
binary binair Invoer uitvoer Invoer en uitvoer gebeurt in binair.
in invoer Invoer Opent het bestand om te lezen.
out uitgang uitgang Opent het bestand om te schrijven.
trunc truncate Invoer uitvoer Verwijdert de inhoud van het bestand bij het openen.
ate aan het einde Invoer Gaat naar het einde van het bestand bij het openen.

Opmerking: als u de binary modus instelt, kunnen de gegevens precies worden gelezen / geschreven zoals ze zijn; Als u dit niet instelt, wordt de vertaling van het teken '\n' van de nieuwe regel naar / van een platformspecifieke einde van de regelreeks mogelijk.

Op Windows is het einde van de regelreeks bijvoorbeeld CRLF ( "\r\n" ).
Schrijf: "\n" => "\r\n"
Lezen: "\r\n" => "\n"

Een bestand sluiten

Een bestand expliciet sluiten is zelden nodig in C ++, omdat een bestandsstream het bijbehorende bestand automatisch in de destructor sluit. Probeer echter de levensduur van een bestandsstream-object te beperken, zodat de bestandsingang niet langer dan nodig open blijft. Dit kan bijvoorbeeld door alle bestandsbewerkingen in een eigen scope te plaatsen ( {} ):

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.

Het expliciet aanroepen van close() is alleen nodig als u later hetzelfde fstream object opnieuw wilt gebruiken, maar het bestand niet tussendoor open wilt houden:

// 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();

Een stroom doorspoelen

Bestandsstreams worden standaard gebufferd, net als vele andere soorten streams. Dit betekent dat bij schrijven naar de stream het onderliggende bestand mogelijk niet meteen wordt gewijzigd. Als u alle gebufferde schrijfbewerkingen onmiddellijk wilt laten plaatsvinden, kunt u de stream spoelen . U kunt dit rechtstreeks doen door de methode flush() aan te roepen of via de std::flush stream-manipulator:

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

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

Er is een std::endl die het schrijven van een nieuwe regel combineert met het doorspoelen van de stream:

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

Bufferen kan de prestaties van het schrijven naar een stream verbeteren. Daarom moeten toepassingen die veel schrijven, onnodig spoelen vermijden. In tegenstelling, als I / O niet vaak wordt uitgevoerd, moeten applicaties overwegen regelmatig te spoelen om te voorkomen dat gegevens vast komen te zitten in het stroomobject.

Een ASCII-bestand in een std :: string lezen

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()`
}

De methode rdbuf() retourneert een pointer naar een streambuf die in buffer kan worden gepusht via de stringstream::operator<< .


Een andere mogelijkheid (gepopulariseerd in Effective STL door Scott Meyers ) is:

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

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

  // Operations on `str`...
}

Dit is leuk omdat het weinig code vereist (en het mogelijk maakt om een bestand rechtstreeks in een STL-container te lezen, niet alleen tekenreeksen), maar kan traag zijn voor grote bestanden.

OPMERKING : de extra haakjes rond het eerste argument voor de tekenreeksconstructor zijn essentieel om het meest lastige parsprobleem te voorkomen.


Tenslotte:

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`...
}

dat is waarschijnlijk de snelste optie (van de drie voorgestelde).

Een bestand in een container lezen

In het onderstaande voorbeeld gebruiken we std::string en operator>> om items uit het bestand te lezen.

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

In het bovenstaande voorbeeld doorlopen we gewoon het bestand met één "item" per keer met behulp van operator>> . Ditzelfde effect kan worden bereikt met de std::istream_iterator , een invoer-iterator die één "item" tegelijk uit de stream leest. Ook kunnen de meeste containers worden gebouwd met behulp van twee iterators, zodat we de bovenstaande code kunnen vereenvoudigen tot:

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

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

We kunnen dit uitbreiden naar elk gewenst objecttype door eenvoudig het object dat we willen lezen op te geven als de sjabloonparameter voor de std::istream_iterator . We kunnen het bovenstaande dus eenvoudig uitbreiden om regels (in plaats van woorden) als volgt te lezen:

// 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>{});

Een `struct` uit een opgemaakt tekstbestand lezen.

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

Output:

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

Een bestand kopiëren

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

Met C ++ 17 is de standaardmanier om een bestand te kopiëren inclusief de <filesystem> -kop en copy_file :

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

De bestandssysteembibliotheek is oorspronkelijk ontwikkeld als boost.filesystem en is uiteindelijk samengevoegd met ISO C ++ vanaf C ++ 17.

Het einde van een bestand controleren in een lus, slechte praktijk?

eof retourneert true alleen na het lezen van het einde van het bestand. Het geeft NIET aan dat de volgende read het einde van de stream is.

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

  f >> buffer;

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

  /* Use `buffer` */
}

Je zou correct kunnen schrijven:

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

  if (f.fail())
    break;

  /* Use `buffer` */
}

maar

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

is eenvoudiger en minder foutgevoelig.

Verdere referenties:

  • std::ws : gooit lege witruimte uit een invoerstream weg
  • std::basic_ios::fail : geeft true als er een fout is opgetreden in de bijbehorende stream

Bestanden schrijven met niet-standaard landinstellingen

Als u een bestand moet schrijven met verschillende standaardinstellingen, kunt u std::locale en std::basic_ios::imbue() gebruiken om dat te doen voor een specifieke bestandsstream:

Richtlijnen voor gebruik:

  • U moet altijd een lokale toepassing op een stream toepassen voordat u het bestand opent.
  • Zodra de stream is doordrenkt, moet je de landinstelling niet wijzigen.

Redenen voor beperkingen: Een bestandsstream inlezen met een landinstelling heeft ongedefinieerd gedrag als de huidige landinstelling niet onafhankelijk van de staat is of niet naar het begin van het bestand verwijst.

UTF-8-streams (en andere) zijn niet onafhankelijk van de staat. Ook kan een bestandsstream met een UTF-8-landinstelling proberen de stuklijstmarkering uit het bestand te lezen wanneer het wordt geopend; dus alleen het openen van het bestand kan tekens uit het bestand lezen en het zal niet aan het begin zijn.

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

Het expliciet overschakelen naar de klassieke "C" -landinstelling is handig als uw programma een andere standaardlandinstelling gebruikt en u een vaste standaard voor het lezen en schrijven van bestanden wilt garanderen. Met een voorkeurstaal "C" schrijft het voorbeeld

78,123.456
78,123.456
78123.456

Als de voorkeurslandinstelling bijvoorbeeld Duits is en daarom een ander nummerformaat gebruikt, schrijft het voorbeeld

78 123,456
78,123.456
78123.456

(let op de komma op de eerste regel).



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow