Leggere una stringa da un file: come gestire l’input in C++
Dopo aver visto come salvare dati su disco, è naturale volerli ricaricare in un secondo momento. In C++, leggere una stringa da un file è un’operazione altrettanto semplice, grazie all’uso della libreria <fstream>
. In questa lezione esploreremo come aprire un file in input, estrarne il contenuto in una variabile di tipo std::string
e quali aspetti considerare per gestire correttamente eventuali errori o formati non previsti.
Perché leggere una stringa da un file
Che si voglia riconfigurare un programma, caricare preferenze utente o riaprire dati precedentemente salvati, la lettura da file rappresenta uno dei meccanismi più immediati di persistenza. Proprio come per la scrittura, la libreria standard C++ fornisce strumenti affidabili, che consentono di interagire con il file system con un codice conciso e di facile manutenzione.
Libreria di base: <fstream>
Per la lettura dei file, la classe fondamentale è std::ifstream
, che opera in modo speculare a std::ofstream
. In fase di inclusione:
#include <iostream>
#include <fstream>
#include <string>
ci assicuriamo di avere a disposizione sia i flussi standard (std::cin
, std::cout
) sia la gestione di file (std::ifstream
, std::ofstream
) e la classe std::string
.
Aprire un file in lettura
Il primo passo è creare un oggetto std::ifstream
indicando il nome del file da cui vogliamo leggere. Possiamo passare un percorso assoluto o relativo, a seconda di dove sia posizionato il file. Per esempio:
std::ifstream fileInput("dati.txt");
if (!fileInput) {
std::cerr << "Errore nell'aprire il file in lettura." << std::endl;
// Gestione dell’errore o termine del programma
}
Se il file non esiste o non abbiamo i permessi di lettura, fileInput
fallirà l’apertura e potremo accorgercene controllando il suo valore in un contesto booleano.
Leggere una singola riga
Una delle modalità più comuni per acquisire stringhe da file è quella di leggere riga per riga. C++ fornisce la funzione std::getline
, che permette di estrarre caratteri dal flusso fino a incontrare un separatore di linea (in genere \n
). Se vogliamo ottenere in una variabile tutto il contenuto di una riga, possiamo fare così:
std::string riga;
if (std::getline(fileInput, riga)) {
// riga contiene la linea letta dal file
std::cout << "Ho letto: " << riga << std::endl;
} else {
std::cerr << "Nessuna linea letta o fine del file raggiunta." << std::endl;
}
In questo esempio, std::getline
restituisce true
se la lettura ha avuto successo, false
se non c’è più nulla da leggere (fine file) o se si è verificato un errore.
Leggere tutte le righe del file
Per leggere più linee, spesso si fa un ciclo while
che termina quando std::getline
non riesce più a restituire dati:
std::string riga;
while (std::getline(fileInput, riga)) {
std::cout << "Riga letta: " << riga << std::endl;
}
Questo ciclo si interrompe non appena viene raggiunta la fine del file (EOF
) o si verificano errori di altro tipo (file corrotto, dischi sconnessi, ecc.).
Lettura di un’unica parola
Se invece di un’intera riga vogliamo leggere “parola per parola”, possiamo usare l’operatore >>
:
std::string parola;
while (fileInput >> parola) {
std::cout << "Parola: " << parola << std::endl;
}
Qui, il flusso si interrompe quando non ci sono più parole da estrarre. Questo approccio si basa sullo spazio (o tab, o newline) come delimitatore, e quindi funziona bene con file che contengono token separati da spazi bianchi.
Gestione degli errori di lettura
In caso di problemi durante la lettura (ad esempio, se il file è stato rimosso a runtime, o c’è un malfunzionamento del disco), il flusso imposta un flag di errore (fail
, bad
). Dopo la lettura, potremmo controllare:
if (!fileInput.good()) {
std::cerr << "Errore nella lettura dal file." << std::endl;
}
Analogamente, fileInput.eof()
restituisce true
se il fileInput ha raggiunto la fine del file. Tuttavia, è più comune strutturare un ciclo basato sull’esito della stessa operazione di input, come visto nei casi precedenti (while (std::getline(fileInput, riga)) { ... }
).
Esempio completo: lettura di una singola riga
Mettiamo insieme un breve programma che apra un file di testo e legga la prima riga al suo interno:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream fileInput("dati.txt");
if (!fileInput) {
std::cerr << "Errore nell'aprire il file in lettura." << std::endl;
return 1;
}
std::string riga;
if (std::getline(fileInput, riga)) {
std::cout << "Prima riga del file: " << riga << std::endl;
} else {
std::cerr << "File vuoto o impossibile leggere la riga." << std::endl;
}
// Possiamo chiudere il file, anche se ofstream e ifstream lo fanno automaticamente al termine del main
fileInput.close();
return 0;
}
Se dati.txt
contiene qualcosa, vedremo la prima riga stampata su std::cout
.
Esempio completo: lettura di tutte le righe
Se volessimo leggere interamente il file, riga dopo riga, possiamo usare il ciclo while
:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream fileInput("dati.txt");
if (!fileInput) {
std::cerr << "Errore nell'aprire il file in lettura." << std::endl;
return 1;
}
std::string riga;
while (std::getline(fileInput, riga)) {
std::cout << "Riga: " << riga << std::endl;
}
// Se qui vogliamo controllare eventuali errori fuori dal loop:
if (!fileInput.eof()) {
std::cerr << "Terminato prima di EOF per un errore sconosciuto." << std::endl;
}
fileInput.close();
return 0;
}
In tal caso, ogni riga verrà letta e stampata fino a raggiungere la fine del file o un errore di input.
Lettura di stringhe con spazi
Uno dei vantaggi di std::getline
rispetto all’operatore >>
è che riesce a mantenere l’intera riga inclusi gli spazi. Se invece usassimo fileInput >> variabile
, la lettura si fermerebbe al primo spazio (o carattere di separazione), restituendo una singola “parola” alla volta. Quindi, se il file contiene frasi o righe separate da newline, l’approccio a std::getline
è quello giusto.
Leggere una stringa da un file in C++ è un’operazione essenziale, spesso speculare rispetto alla scrittura. Con std::ifstream
apriamo il file, verifichiamo che sia andato tutto bene, e usiamo funzioni come std::getline
per estrarre il contenuto in una variabile di tipo std::string
. Quando la lettura è finita o se incontriamo un problema, possiamo controllare i flag di errore o la condizione di fine file. In pochi passaggi, si ottiene così la possibilità di caricare configurazioni, dati utente, log e quant’altro, rendendo le nostre applicazioni più flessibili e persistenti.