Ricerca…


introduzione

L'I / O del file C ++ viene eseguito tramite flussi . Le astrazioni chiave sono:

std::istream per leggere il testo.

std::ostream per scrivere testo.

std::streambuf per leggere o scrivere caratteri.

L'input formattato utilizza l' operator>> .

L'output formattato utilizza l' operator<< .

Gli stream usano std::locale , ad es. Per i dettagli sulla formattazione e per la traduzione tra codifiche esterne e codifica interna.

Altro sugli stream: <iostream> Library

Aprire un file

L'apertura di un file viene eseguita allo stesso modo per tutti e 3 i flussi di file ( ifstream , ofstream e fstream ).

Puoi aprire il file direttamente nel costruttore:

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.

In alternativa, è possibile utilizzare la funzione membro open() del flusso di file 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.

Si dovrebbe sempre verificare se un file è stato aperto correttamente (anche durante la scrittura). Gli errori possono includere: il file non esiste, il file non ha i diritti di accesso corretti, il file è già in uso, si sono verificati errori del disco, unità disconnessa ... Il controllo può essere eseguito come segue:

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

Quando il percorso del file contiene barre retroverse (ad esempio, su sistema Windows) dovresti eseguire correttamente l'escape:

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

o usa letterale crudo:

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

o usare invece le barre in avanti:

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

Se si desidera aprire il file con caratteri non ASCII nel percorso su Windows al momento, è possibile utilizzare un argomento del percorso dei caratteri ampio non standard :

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

Lettura da un file

Esistono diversi modi per leggere i dati da un file.

Se si conosce come vengono formattati i dati, è possibile utilizzare l'operatore di estrazione del flusso ( >> ). Supponiamo che tu abbia un file chiamato foo.txt che contiene i seguenti dati:

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

Quindi è possibile utilizzare il seguente codice per leggere i dati dal file:

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

L'operatore di estrazione del flusso >> estrae ogni carattere e si arresta se trova un carattere che non può essere memorizzato o se è un carattere speciale:

  • Per i tipi di stringa, l'operatore si ferma in uno spazio bianco ( ) o su una nuova riga ( \n ).
  • Per i numeri, l'operatore si ferma con un carattere non numerico.

Ciò significa che anche la seguente versione del file foo.txt verrà letta con successo dal codice precedente:

John 
Doe 25
4 6 1987


Jane
Doe 
15 5
24
1976

L'operatore di estrazione del flusso >> restituisce sempre lo stream a esso assegnato. Pertanto, più operatori possono essere concatenati insieme per leggere i dati consecutivamente. Tuttavia, uno stream può anche essere usato come espressione booleana (come mostrato nel ciclo while nel codice precedente). Questo perché le classi di flusso hanno un operatore di conversione per il tipo bool . Questo operatore bool() restituirà true fintanto che lo stream non ha errori. Se un flusso entra in uno stato di errore (ad esempio, poiché non è possibile estrarre altri dati), l'operatore bool() restituirà false . Pertanto, il ciclo while nel codice precedente verrà chiuso dopo che il file di input è stato letto fino alla fine.

Se desideri leggere un intero file come una stringa, puoi usare il seguente codice:

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

Questo codice riserva spazio per la string al fine di ridurre le allocazioni di memoria non necessarie.

Se si desidera leggere un file riga per riga, è possibile utilizzare la funzione 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.
}

Se vuoi leggere un numero fisso di caratteri, puoi usare la funzione membro del flusso read() :

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

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

Dopo aver eseguito un comando di lettura, è necessario verificare sempre se è stato impostato il flag di stato di errore failbit , in quanto indica se l'operazione ha avuto esito negativo o meno. Questo può essere fatto chiamando la funzione membro fail() del file stream fail() :

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

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

Scrivere su un file

Esistono diversi modi per scrivere in un file. Il modo più semplice è utilizzare un flusso di file di output ( ofstream ) insieme all'operatore di inserimento del flusso ( << ):

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

Invece di << , è anche possibile utilizzare la funzione membro write() del flusso del file di output write() :

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

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

Dopo aver scritto su uno stream, è necessario controllare sempre se è stato impostato lo stato di errore flag badbit , poiché indica se l'operazione ha avuto esito negativo o meno. Questo può essere fatto chiamando la funzione membro del flusso del file di output bad() :

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

Modalità di apertura

Quando si crea un flusso di file, è possibile specificare una modalità di apertura. Una modalità di apertura è fondamentalmente un'impostazione per controllare come il flusso apre il file.

(Tutte le modalità possono essere trovate nel namespace std::ios .)

Una modalità di apertura può essere fornita come secondo parametro per il costruttore di un flusso di file o per la sua funzione membro open() :

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

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

Si noti che è necessario impostare ios::in o ios::out se si desidera impostare altri flag in quanto non sono impostati in modo implicito dai membri iostream sebbene abbiano un valore predefinito corretto.

Se non si specifica una modalità di apertura, vengono utilizzate le seguenti modalità predefinite:

  • ifstream - in
  • ofstream - out
  • fstream - in e out

Le modalità di apertura dei file che puoi specificare in base alla progettazione sono:

Modalità Senso Per Descrizione
app aggiungere Produzione Aggiunge i dati alla fine del file.
binary binario Input Output L'input e l'output sono fatti in binario.
in ingresso Ingresso Apre il file per la lettura.
out produzione Produzione Apre il file per la scrittura.
trunc troncare Input Output Rimuove il contenuto del file all'apertura.
ate alla fine Ingresso Va alla fine del file quando si apre.

Nota: l' impostazione della modalità binary consente di leggere / scrivere i dati esattamente così come sono; non impostandolo consente la traduzione del carattere '\n' nuova riga in / da una sequenza di fine riga specifica della piattaforma.

Ad esempio su Windows la sequenza di fine riga è CRLF ( "\r\n" ).
Scrivi: "\n" => "\r\n"
Leggi: "\r\n" => "\n"

Chiusura di un file

Raramente chiudere un file è raramente necessario in C ++, in quanto un flusso di file chiuderà automaticamente il suo file associato nel suo distruttore. Tuttavia, si dovrebbe cercare di limitare la durata di un oggetto flusso di file, in modo che non mantenga l'handle del file aperto più a lungo del necessario. Ad esempio, ciò può essere fatto inserendo tutte le operazioni sui file in un proprio ambito ( {} ):

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.

Chiamare close() esplicitamente è necessario solo se si desidera riutilizzare lo stesso oggetto fstream secondo momento, ma non si vuole mantenere il file aperto tra:

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

Flushing a stream

I flussi di file sono bufferizzati per impostazione predefinita, come lo sono molti altri tipi di flussi. Ciò significa che le scritture sul flusso potrebbero non modificare immediatamente il file sottostante. In oder per forzare immediatamente tutte le scritture bufferizzate, è possibile svuotare il flusso. Puoi farlo direttamente invocando il metodo flush() o tramite il manipolatore di flusso std::flush :

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

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

C'è un manipolatore di flusso std::endl che combina la scrittura di una nuova riga con lo streaming del flusso:

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

Il buffering può migliorare le prestazioni della scrittura su uno stream. Pertanto, le applicazioni che richiedono molta scrittura dovrebbero evitare il risciacquo inutilmente. Contrariamente, se l'I / O viene eseguito di rado, le applicazioni dovrebbero prendere in considerazione il flushing frequentemente per evitare che i dati si blocchino nell'oggetto stream.

Lettura di un file ASCII in una stringa std ::

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

Il metodo rdbuf() restituisce un puntatore a uno streambuf che può essere inserito nel buffer tramite la funzione membro stringstream::operator<< .


Un'altra possibilità (resa popolare da Effective STL di Scott Meyers ) è:

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

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

  // Operations on `str`...
}

Questo è bello perché richiede poco codice (e consente di leggere un file direttamente in qualsiasi contenitore STL, non solo stringhe), ma può essere lento per i file di grandi dimensioni.

NOTA : le parentesi extra attorno al primo argomento del costruttore di stringhe sono essenziali per prevenire il problema di analisi più irritante .


Ultimo, ma non per importanza:

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

che è probabilmente l'opzione più veloce (tra le tre proposte).

Lettura di un file in un contenitore

Nell'esempio seguente usiamo std::string e operator>> per leggere gli elementi dal file.

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

Nell'esempio sopra stiamo semplicemente iterando attraverso il file leggendo un "oggetto" alla volta usando l' operator>> . Questo stesso effetto può essere ottenuto usando lo std::istream_iterator che è un iteratore di input che legge un "elemento" alla volta dallo stream. Inoltre, la maggior parte dei contenitori può essere costruita utilizzando due iteratori in modo da semplificare il codice sopra riportato per:

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

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

Possiamo estendere questo per leggere qualsiasi tipo di oggetto che ci piace semplicemente specificando l'oggetto che vogliamo leggere come parametro template per lo std::istream_iterator . Quindi possiamo semplicemente estendere quanto sopra per leggere le righe (piuttosto che le parole) in questo modo:

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

Leggere una `struct` da un file di testo formattato.

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

Produzione:

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

Copia di un file

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

Con C ++ 17 il modo standard per copiare un file include l'intestazione <filesystem> e l'uso di copy_file :

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

La libreria del filesystem era originariamente sviluppata come boost.filesystem e infine fusa con ISO C ++ a partire da C ++ 17.

Controllare la fine del file all'interno di una condizione di loop, cattiva pratica?

eof restituisce true solo dopo aver letto la fine del file. NON indica che la prossima lettura sarà la fine del flusso.

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

  f >> buffer;

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

  /* Use `buffer` */
}

Potresti scrivere correttamente:

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

  if (f.fail())
    break;

  /* Use `buffer` */
}

ma

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

è più semplice e meno soggetto a errori.

Ulteriori riferimenti:

  • std::ws : elimina gli spazi bianchi iniziali da un flusso di input
  • std::basic_ios::fail : restituisce true se si è verificato un errore nel flusso associato

Scrittura di file con impostazioni internazionali non standard

Se è necessario scrivere un file utilizzando impostazioni internazionali diverse per l'impostazione predefinita, è possibile utilizzare std::locale e std::basic_ios::imbue() per farlo per un flusso di file specifico:

Guida per l'uso:

  • Devi sempre applicare un locale a uno stream prima di aprire il file.
  • Una volta che il flusso è stato imbevuto non è necessario modificare le impostazioni internazionali.

Motivi per le restrizioni: l' importazione di un flusso di file con una locale ha un comportamento non definito se le impostazioni internazionali correnti non sono indipendenti dallo stato o non puntano all'inizio del file.

I flussi UTF-8 (e altri) non sono indipendenti dallo stato. Anche un flusso di file con una locale UTF-8 può provare a leggere l'indicatore BOM dal file quando viene aperto; quindi basta aprire il file per leggere i caratteri dal file e non sarà all'inizio.

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

Passare in modo esplicito alla classica "C" locale è utile se il tuo programma usa una diversa localizzazione predefinita e vuoi garantire uno standard fisso per leggere e scrivere file. Con una locale preferita "C", scrive l'esempio

78,123.456
78,123.456
78123.456

Se, ad esempio, la locale preferita è il tedesco e quindi utilizza un formato numerico diverso, l'esempio scrive

78 123,456
78,123.456
78123.456

(notare la virgola decimale nella prima riga).



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow