PDA

Visualizza la versione completa : [C++] Operazioni I/O file binario


Amoroso2687
09-05-2012, 20:18
Salve a tutti, vorrei togliermi alcune curiosità...

Considerando un file binario, è corretto l'uso degli operatori ">>" e "<<" per leggere e scrivere da/in un file?

Questa mattina ho avuto un dibattito con la mia prof di informatica.. ve ne sarei molto grato se mi toglieste questo dubbio...

Who am I
09-05-2012, 20:58
Non vedo la differenza tra un file di testo e un file binario.
Prova a fare un esempio di programma.

Amoroso2687
09-05-2012, 23:27
Ciao, innanzi tutto grazie della risposta :)
ti posto in seguito una parte del codice.. come puoi notare, ho considerato un file binario e ho usato l'operatore ">>"(lettura) e "<<"(scrittura)... Questo mi ha portato ad avere una lunga discussione con la mia prof. di informatica, che riteneva inappropriato l'uso degli operatori ">>" "<<" in un contesto in cui si operava su file binari..
Non ce bisogno di dirti che io mi sono trovato in totale disaccordo con lei (il programma mi funziona una meraviglia).. e vorrei dei pareri da persone competenti in materia.
Quindi, l'uso di questi operatori è scorretto? può comportare dei problemi a livello di gestione di stringhe? sarebbe meglio usare le funzioni file.write e file.read oppure sono praticamente identici?



void Visualizza()
{
bool VerificaData(int G, int M, int A);

File.open("Dati.dat", ios::in|ios::binary);

do
{
Verifica=true;
cout << "Inserisci la data di oggi: (GG/MM/AAAA) " << endl;
cout << "Giorno: ";
cin >> Data[2].Giorno;
cout << "Mese: ";
cin >> Data[2].Mese;
cout << "Anno: ";
cin >> Data[2].Anno;

VerificaData(Data[2].Giorno,Data[2].Mese,Data[2].Anno);
}while(Verifica==false);

while(File.eof()!=true) //quando termina il file, esci dal ciclo
{
Posiz=File.tellg();
File>>Data[3].Giorno>>Data[3].Mese>>Data[3].Anno;
File.ignore(504, '\n');

if((Data[2].Giorno==Data[3].Giorno) && (Data[2].Mese==Data[3].Mese) && (Data[2].Anno==Data[3].Anno))
{
Trovato=true;
File.seekg(Posiz);
File.ignore(17, '#');
File.getline(DescrizioneImpegno.Descrizione,500);
cout << DescrizioneImpegno.Descrizione << endl;
break;
}
}

if(Trovato==false)
{
cout << "Non è stato trovato nessun impegno per questo giorno!" << endl;
}

File.close();

main();
}

void Carica()
{
bool VerificaData(int G, int M, int A);

do
{
Verifica=true;
cout << "Inserisci la data dell'impegno: (GG/MM/AAAA) " << endl;
cout << "Giorno: ";
cin >> Data[0].Giorno;
cout << "Mese: ";
cin >> Data[0].Mese;
cout << "Anno: ";
cin >> Data[0].Anno;

VerificaData(Data[0].Giorno,Data[0].Mese,Data[0].Anno);
}while(Verifica==false);

File.open("Dati.dat", ios::out|ios::app|ios::binary);
ScriviData(Data[0].Giorno,Data[0].Mese,Data[0].Anno);

cout << endl << "Descrizione Impegno: " << endl;
cout << "Ora Scadenza: (OO:MM) ";
cin >> DescrizioneImpegno.Ora;
File << DescrizioneImpegno.Ora << " ";
cout << "Descrizione: (aggiungere '+ ' prima della scrittura) (max: 500ch) " << endl;
cin >> DescrizioneImpegno.Descrizione;
gets(DescrizioneImpegno.Descrizione); //accetta gli spazi;
File << " #" << DescrizioneImpegno.Descrizione << "# " << endl;


File.close();

main();

}

Who am I
09-05-2012, 23:47
Ci sono delle leggere differenze che però possono diventare dei motivi sostanziali per scegliere l' uno o l' altro.
Innanzitutto l' operatore >> legge una stringa alla volta e considera gli spazi come separatori.Per esempio se leggi da stdin o da file la stringa: "ciao come stai?", se sai che è lunga 16 caratteri usi la read e leggi esattamente 16 byte, invece l' operatore >> legge solo "ciao" perché considera lo spazio come un separatore.
Altra differenza è che una classe potrebbe fare overloading dell' operatore >> o << e quindi causare un input/output diverso dal metodo read/write di ifstream e ofstream.
Però vedo che hai usato anche la getline(), in generale visto che ti funziona (almeno spero :afraid: ) e che non hai usato classi particolari ne il tipo string, direi che non c'è motivo per considerare questo programma errato, a meno che non trovi degli errori effettivi.

Però aspetto anche la conferma di qualche altro utente.

shodan
10-05-2012, 00:20
A livello umano, un file di testo è qualcosa che si riesce a leggere, un file binario è un'accozzaglia di simboli strani (o numeri), di cui l'insieme [A-Z a-z 0-9], più varia punteggiatura, rappresenta un sottoinsieme limitato di bytes.

A livello di file, la distinzione tra testo e binario si basa sulla conversione di \n in \r\n in scrittura oppure di \r\n in lettura. Valida però solo in quei sistemi operativi che richiedono due caratteri per un newline (Windows per esempio).

A livello di operatori >> e << sono effettuate altre conversioni (ad esempio i decimali vengono separati da virgola invece che dal punto se viene cambiato il locale), mentre in binario hai a disposizione l'intero file (\r\n compresi che in binario sono valori validi).

Non hai specificato il sistema operativo con cui lavori, ma se è windows prova ad aprire il file col block notes e vedi.
Specificando ios::binary hai disabilitato le conversioni implicite che fa il runtime, per cui dovresti avere tutto su una linea invece di più linee (sempre parlando di windows ovviamente).
Questo dando per scontato se il file è umanamente intelleggibile (testo).
Se il file è realmente binario e dentro c'è scritto 05F743B9 (3108205573 come int) su macchine little endian, con >> non ottieni 3108205573, ma l'invalidazione dello stream di input (in virtù della F dopo 05).
Altri effetti collaterali sono descritti da Who.

Per farla breve la tua prof ha ragione.

Ultima osservazione:
io ti boccierei solo perché hai usato:


gets(DescrizioneImpegno.Descrizione); //accetta gli spazi;

:D

Amoroso2687
10-05-2012, 14:33
Grazie mille delle risposte.
shodan, se non vado in off topic.. vorrei delle tue considerazioni sulla tua ultima osservazione :zizi:

shodan
10-05-2012, 17:22
gets è l'unica funzione deprecata ufficialmente sia dallo standard C sia dallo standard C++ .
Non fa nessun controllo sulla lunghezza dell'input, pertanto è soggetta a buffer overflow in fase di lettura. Se devi usare un input simile, usa fgets, passando lunghezza del buffer e stdin come parametro.
In ogni caso mischiare input C mode e input C++ è sempre concettualmente sbagliato. O usi l'uno o usi l'altro.

Amoroso2687
10-05-2012, 18:27
Ti ringrazio infinitamente, mi sei stato di grande aiuto. Sono giovane, mi ha fatto piacere questa tua considerazione.. purtroppo ho ancora molta strada da fare :zizi:

Loading