HOWTO · C++

Leggi il file CSV in C++

Questo articolo mostra come leggere un file CSV in C++

In questa pagina

Per leggere in C++ un semplice file delimitato da virgole, leggi una riga alla volta con std::getline, quindi suddividila in campi con una seconda chiamata a std::getline. Questo metodo funziona quando ogni riga fisica corrisponde a un record e le virgole separano soltanto i campi. L’esempio seguente crea un piccolo file di input, così puoi compilare ed eseguire il programma senza preparare un file di dati separato.

Leggere un file CSV con std::getline e std::istringstream

Il programma scrive prima due record in grades.csv, poi apre il file per leggerlo. Il std::getline esterno legge una riga fisica in un std::string; il std::istringstream la trasforma in uno stream e il std::getline interno usa la virgola come delimitatore per estrarre ogni campo. Ogni record diventa un std::vector<std::string>, mentre l’insieme dei record è un altro vettore. Così si mantiene l’ordine originale senza usare una mappa indicizzata da interi quando le righe sono semplicemente sequenziali.

Esegui questo programma autonomo:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

#include <algorithm>
#include <cctype>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

std::string trimEnds(const std::string& value) {
    const auto first = std::find_if_not(value.begin(), value.end(),
        [](unsigned char ch) { return std::isspace(ch); });
    const auto last = std::find_if_not(value.rbegin(), value.rend(),
        [](unsigned char ch) { return std::isspace(ch); }).base();
    if (first >= last) {
        return "";
    }
    return std::string(first, last);
}

int main() {
    const std::string path = "grades.csv";
    {
        std::ofstream sample(path);
        if (!sample) {
            std::cerr << "Could not create input file\n";
            return 1;
        }
        sample << "Name, Score\nAda Lovelace, 98 \nGrace Hopper, 95\n";
    }

    std::ifstream input(path);
    if (!input) {
        std::cerr << "Could not open input file\n";
        return 1;
    }

    std::vector<std::vector<std::string>> rows;
    std::string record;
    while (std::getline(input, record)) {
        std::istringstream line(record);
        std::vector<std::string> fields;
        std::string field;
        while (std::getline(line, field, ',')) {
            fields.push_back(trimEnds(field));
        }
        rows.push_back(fields);
    }

    for (const auto& row : rows) {
        std::cout << row[0] << " | " << row[1] << '\n';
    }
    return 0;
}

Il risultato fornito per l’esecuzione riuscita è:

L’esempio visualizza ogni nome accanto al relativo punteggio. Le etichette Name e Score nel risultato restano in inglese perché il blocco di output è mantenuto identico all’originale.

Name | Score
Ada Lovelace | 98
Grace Hopper | 95

Se la creazione o l’apertura del file non riesce, il programma scrive un messaggio di errore in std::cerr e restituisce lo stato 1; in condizioni normali restituisce lo stato 0. Nel tuo programma, verifica che lo stream di input sia stato aperto correttamente prima di leggere. Se il file proviene dall’esterno del programma, indica il percorso e gestisci l’errore di apertura secondo le esigenze della tua applicazione, senza presumere che il file esista.

Gestire gli spazi nei campi CSV

La gestione degli spazi è una scelta relativa ai dati, non un effetto automatico della suddivisione. trimEnds rimuove gli spazi solo dall’inizio e dalla fine di ogni campo estratto. Conserva gli spazi interni in un valore come Ada Lovelace. La conversione in unsigned char prima di chiamare std::isspace evita di passare a questa funzione di classificazione dei caratteri un valore char semplice negativo.

In alcuni file gli spazi dopo una virgola sono considerati formattazione; in altri fanno parte del campo. Applica la rimozione degli spazi solo se è coerente con il contratto dei dati che devi rispettare. Eliminare tutti gli spazi unirebbe anche le parole di un nome o di un indirizzo; rimuoverli da entrambe le estremità modifica invece il contenuto iniziale e finale. Se gli spazi hanno significato nei dati di origine, conserva il campo così com’è oppure applica una regola più specifica, per esempio rimuovendo solo gli spazi ASCII.

Il ciclo di suddivisione ha anche una semplice conseguenza per i campi vuoti: due virgole consecutive tra altri contenuti producono una stringa vuota tra loro. Una virgola finale non fa aggiungere a questo ciclo std::getline un ultimo campo vuoto; se il formato di input lo richiede, gestisci esplicitamente il delimitatore finale. Conserva o rifiuta i valori vuoti in base alle colonne previste. Se una riga contiene meno campi dell’intestazione, gestisci esplicitamente il caso prima di accedere agli elementi: il breve ciclo di visualizzazione precedente presuppone che siano presenti entrambe le colonne.

Comprendere i limiti dell’analisi basata sui delimitatori

Questo metodo divide ogni riga fisica in corrispondenza di tutte le virgole. Non interpreta le virgolette del formato CSV. Per esempio, un valore come "Paris, France" viene diviso in due parti, anche se un lettore CSV dovrebbe trattare il testo tra virgolette come un unico campo. Le virgolette di escape all’interno dei campi tra virgolette non vengono decodificate e una nuova riga all’interno di un campo tra virgolette viene scambiata per la fine di un record. Inoltre, i record possono contenere un numero diverso di campi: non presumere che ogni riga contenga lo stesso numero di virgole.

Usa questo metodo con dati semplici e controllati, le cui regole garantiscono un record per riga e nessun delimitatore tra virgolette. Per file CSV generici, usa un parser che implementi il dialetto dei dati ricevuti, comprese le regole per virgolette ed escape. Dopo l’analisi, verifica comunque il numero e il significato dei campi richiesti dalla tua applicazione. Le operazioni standard sugli stream mostrate qui gestiscono la lettura dei file e delle righe, ma da sole non implementano l’intera grammatica del formato CSV.