Moro!
Tässä ongelma-aliohjelma:
bool tiedostoTallennus(int &oikeatunnusAl, long double &alkusaldoAl) { struct TIEDOT //TIEDOT-tyyppisen tietueen luonti { int tunnus; long double saldo; }; TIEDOT tietue = {oikeatunnusAl, alkusaldoAl}; bool loytyi = false; //if (!tdsto.is_open()) //Jos tiedostoa ei vielä ole fstream tdsto("tiedot.dat", ios::binary|ios::in|ios::out|ios::app); //tdsto-olion luonti ja tiedoston avaus (luku&kirjoitus) if (tdsto.is_open()) //avaus onnistui { tdsto.seekg(0); //Tunnuksen etsintä alkaa alusta tdsto.clear(); //Virhebitit pois päältä loytyi = 0; while (tdsto.peek() != EOF && loytyi == false) { tdsto.read((char *)&tietue, sizeof(TIEDOT)); //Ongelmakohta if (oikeatunnusAl == tietue.tunnus) { cout<<endl<<"Tunnus on jo olemassa!"<<endl; loytyi = true; } } if (loytyi == false) //Kirjoitetaan uusi tunnus ja alkusumma tiedostoon { tdsto.clear(); tdsto.write((char *)&tietue, sizeof(TIEDOT)); //Kirjoittaa uudet tiedot tiedostoon } } else { cout<<"Avaus ei onnistu"; exit(-1); } tdsto.close(); return loytyi; }
Tarkoitus on siis tallettaa tiedot-tyyppisiä tietueita binääritiedostoon. Aliohjelma saa parametreinä tunnuksen ja alkusaldon. Ohjelman pitäisi tallettaa tunnus ja alkusaldo tiedostoon viimeiseksi ELLEI tunnus jo löydy tiedostosta. Ongelmakohta on merkitty kommenteilla koodiin. Kohdassa if (oikeatunnusAl == tietue.tunnus) vertaillaan parametrinä saatua tunnusta tietueen tunnukseen. Ongelma on, että tietue.tunnus näyttäisi sisältävän aina ensimmäisen tallennetun arvon. Tarkoitus olisi käydä läpi koko tiedoston tunnus-kentät ja vertailla parametrinä saatuun tunnukseen. Esimerkiksi jos kolmantena on talletettu tunnus 3201 ja viidentenä yritetään tallentaa samaa, ohjelma huomaa (eli oikeatunnusAl == tietue.tunnus saa arvon true). Missä moka?
Mitä jos tallentaisit alkuvaiheessa tiedot ihan selkeänä tekstinä? Vertailun saattaisi olla helpompaa. Lisäksi ohjelman rakenteen kannalta olisi selkeämpää olla funktio, joka tallentaa halutut tiedot. Lisäksi tuo tietojen tarkistus olisi hyvä erottaa tuosta kirjoituksesta. Ohjelman rakenne voisi olla seuravanlainen.
#include <iostream> #include <fstream> bool onko_samanlaista(&etsittava); void tallenna(&tallennetava); struct TIEDOT{ int tunnus; long double saldo; }; int main(){ /** * Kysy käyttäjältä tunnus ja alkusaldo * Luo uusi tietue * Tarkista onko samaa tunnusta jo olemassa */ luotu_tietue = {kysytty_tunnus, kysytty_saldo}; if(onko_samanlaista(luotu_tietue)){ tallenna(luotu_tietue); }else{ /** * Valita samanlaisesta tunnuksesta */ } return EXIT_SUCCESS; }
Yksi selvä vika on, että tarkistusvaiheessa luet tietoja samaan tietueeseen, jonka myöhemmin kirjoitat tiedostoon. Toisin sanoen tallennatkin aina viimeisen tietueen uudestaan. Lisäksi koodissasi on monenlaista turhaa.
Kun kerran käytössä on C++, niin käytetään sitä sitten kunnolla. :)
#include <stdexcept> #include <fstream> #include <iostream> struct TIEDOT { int tunnus; long double saldo; }; void lisaa(int tunnus, long double saldo) { std::fstream tiedosto("tiedot.dat", std::ios::binary | std::ios::in | std::ios::out | std::ios::app); if (!tiedosto.is_open()) { throw std::runtime_error("Avaus ei onnistu"); } TIEDOT uusi = {tunnus, saldo}; TIEDOT tmp; while (tiedosto.read((char*)&tmp, sizeof(TIEDOT))) { if (tmp.tunnus == uusi.tunnus) { throw std::logic_error("Tunnus löytyi jo"); } } tiedosto.clear(); tiedosto.write((const char*)&uusi, sizeof(TIEDOT)); if (!tiedosto.good()) { throw std::runtime_error("Tallennus ei onnistunut"); } } int main() { // Laitetaan vähän testidataa aluksi... lisaa(1, 3.12); lisaa(2, 7.2); lisaa(3, 0.14); // Tämä heittää poikkeuksen, kolmonen meni jo. try { lisaa(3, 7); } catch (std::exception& e) { std::cout << "Virhe: " << e.what() << std::endl; } }
Kiitoksia näistä vastauksista! Etenkin Metabolix antoi selventävän vastauksen. Sain omastani muokattua toimivan. :)
Aihe on jo aika vanha, joten et voi enää vastata siihen.