C++ – Esempi di gestione di file di testo su dischi: i file CSV

Scritto da Gianjey | 17 ottobre, 2010 18:08

cplusplus

Come esempi di applicazione della manipolazione di dati su memorie di massa, viene proposto un programma per la conservazione di dati in un file di tipo CSV, e un programma che si occupa della lettura dei dati così conservati.

I file CSV (Comma Separated Values) sono file, di tipo testo, in cui in ogni riga c’è una registrazione, per esempio i dati di un libro. I dati di tipo stringa (titolo, autore, editore) sono racchiusi fra due caratteri doppio apice, i dati numerici (prezzo) non sono racchiusi fra due caratteri doppio apice. I vari dati del singolo libro sono separati dal carattere virgola, da cui il nome del tipo di file. L’importanza dei file di questo tipo dipende dal fatto che è comune che i programmi che si occupano di gestire dati (programmi di database, tabelloni elettronici) prevedano funzionalità di importa/esporta di dati in file formato CSV e quindi si può utilizzare un semplice programma in C++ per leggerli, elaborarli in qualsiasi modo e riscriverli in modo da poterli rileggere con un applicativo qualsiasi che preveda l’importazione di dati da un file CSV. Anche se l’applicativo che si utilizza non prevede l’elaborazione che serve, questa si può aggiungere scrivendo un opportuno programma in C++.

Il primo programma proposto crea su disco un file che contiene i dati, dei libri, immessi da tastiera:

#include <iostream>
#include <string>
#include <fstream>	/*1*/
using namespace std;

struct libro {
  string titolo;
  string autore;
  string editore;
  float prezzo;
};

int main()
{
  ofstream datiLib;	/*2*/
  libro libtemp;
  bool continua=true;

  datiLib.open("libri.txt",ios::app);	/*3*/

  while(continua){
    cout << "Titolo :";
    getline(cin,libtemp.titolo);	/*4*/
    if(libtemp.titolo=="")	/*5*/
      break;
    datiLib << "\"" << libtemp.titolo << "\"" << ",";	/*6*/
    cout << "Autore :";
    getline(cin,libtemp.autore);	/*4*/
    datiLib <<  "\"" << libtemp.autore << "\"" << ",";	/*6*/
    cout << "Editore :";
    getline(cin,libtemp.editore);	/*4*/
    datiLib <<  "\"" << libtemp.editore << "\"" << ",";	/*6*/
    cout << "Prezzo :";
    cin >> libtemp.prezzo;	/*4*/
    cin.ignore();
    datiLib << libtemp.prezzo << endl;	/*6*/
  }
  datiLib.close();	/*7*/

  return 0;
}

L’inclusione 1 permette l’utilizzo degli oggetti che permettono la gestione dei flussi su memorie di massa.

Nella 2 si dichiara una variabile (il nome, ovviamente, è a scelta del programmatore) di tipo ofstream (flusso di output), laddove, per la 3, si associa a detta variabile il file su disco libri.txt. Il flusso è aperto in modalità append (ios::app): se il file esiste, i nuovi inserimenti vengono aggiunti in coda, se non esiste, viene creato. Se invece il flusso è aperto in modalità output (ios::out), il file, anche se esistente, viene rigenerato e i dati eventualmente presenti sono persi.

Le 4 permettono di inserire i dati da tastiera. L’inserimento termina (5) quando si preme Invio a vuoto, in risposta alla richiesta di input del titolo del libro.

Con le 6 i dati vengono inviati al file attraverso il flusso datiLib. L’utilizzo è identico a quello del flusso cout che permette l’invio al video. La differenza è data sostanzialmente dalla 3: il concetto astratto di flusso permette di trattare, allo stesso modo, entità diverse (tastiera,video, file su disco). I singoli dati sono mandati al flusso, ognuno racchiuso da una coppia di caratteri doppio apice e separati da virgole. L’ultimo (il prezzo) è concluso dal carattere di fine linea (endl).

Nella 7 si interrompono i collegamenti con il file esterno. Il metodo close() chiude lo stream datiLib. Il metodo è il simmetrico di open().

Anche il programma per la lettura dei dati dal file non si differenzia in maniera sostanziale da un qualsiasi programma che legge dati da un flusso legato alla tastiera:

#include <iostream>
#include <string>
#include <fstream>
using namespace std;

struct libro {
  string titolo;
  string autore;
  string editore;
  float prezzo;
};

libro estraeCampi(string);	/*1*/

int main()
{
  ifstream datiLib;	/*2*/
  libro temp;
  string riga;

  // legge i dati dei libri conservati in un file CSV

  datiLib.open("libri.txt");	/*3*/
  while(getline(datiLib,riga)){	/*4*/

    // estrae campi

    temp = estraeCampi(riga);	/*5*/

    // mostra i risultati

    cout << temp.titolo << " - " << temp.autore << " - " 	/*6*/
         << temp.editore << " - " << temp.prezzo << endl;

  };
  datiLib.close();

  return 0;
}

// Estrae i campi da una riga letta dal file CSV

libro estraeCampi(string r)
{
  libro temp;
  int pos;

  // estrazione campi di tipo stringa

  pos = r.find(',');	/*7*/
  temp.titolo = r.substr(1,pos-2);	/*8*/
  r = r.erase(0,pos+1);	/*9*/

  pos = r.find(',');	/*7*/
  temp.autore = r.substr(1,pos-2);	/*8*/
  r = r.erase(0,pos+1);	/*9*/

  pos = r.find(',');	/*7*/
  temp.editore = r.substr(1,pos-2);	/*8*/
  r = r.erase(0,pos+1);	/*9*/

  // estrazione prezzo

  temp.prezzo = atof(r.c_str());	/*10*/

  return temp;
}

Il programma utilizza una funzione (1) per separare i vari campi dalla riga letta dal file.

Nella 2 viene dichiarata una variabile di tipo ifstream (stream di input) a cui si associa il file libri.txt (3). In questo caso, a differenza del flusso di output, non è necessario specificare la modalità di apertura: in output i dati si possono scrivere in un nuovo file o accodare ad un file esistente, in input si leggono i dati uno di seguito all’altro e non esiste altra alternativa. È possibile, tuttavia anche se superfluo, specificare la modalità ios::in.

Il programma, sostanzialmente, legge le righe esistenti nel file CSV finché è possibile (4), separa i componenti della riga (5) e stampa i dati del libro (6).

La funzione per l’estrazione dei singoli dati del libro dalla riga, cerca la posizione della virgola (7), estrae la quantità di caratteri, ripulita dai doppi apici iniziali e finali, di cui è composto il singolo dato (8) e, per semplificare la prossima estrazione, elimina (9) i caratteri estratti compresi doppi apici e virgola separatrice.

Il dato di tipo numerico è ottenuto richiamando il metodo c_str sulla stringa e convertendola in float (10).

 Indice corso: C++ Appunti di Programmazione Fonte: prof. Nunzio Brugaletta 

Lascia un Commento