A megoldásnál bemutatjuk a kivételkezelés lehetőségeit is. Ezt a félév első felében hagyjuk figyelmen kívül.
A Student.h állomány tartalma:
#include <string>
#include <cstring>
class Student
{
static const unsigned neptun_size = 7;
char neptun[neptun_size]; // Ehelyett is jobb lenne a string, de a példa kedvéért ezt is megmutatjuk
unsigned mark; // Érdemjegy> az unsignde charral több baj van, mint így (minden I/O karakterként kezeli)
public:
std::string name; // Ezt nem akarjuk korlátozni
Student(std::string name = "", char neptun[] ="xxxxxx", unsigned mark=1):name(name)
{
setNeptun(neptun);
setMark(mark);
}
void setNeptun(char neptun[]);
const char * getNeptun()const {return neptun;}
void setMark(unsigned mark);
unsigned char getMark()const{return mark;}
// A visszatérés a ciklusba szervezhetőség miatt, ott lehet az adatfolyam állapotát vizsgálni
std::istream& readBinary(std::istream& is);
void writeBinary(std::ostream& os)const;
// A visszatérés a ciklusba szervezhetőség miatt, ott lehet az adatfolyam állapotát vizsgálni
std::istream& readText(std::istream& is);
void writeText(std::ostream& os)const;
};
A Student. cpp állomány tartalma:
#include "Student.h"
#include <stdexcept>
#include <iostream>
using namespace std;
void Student::setNeptun(char neptun[])
{
if(strlen(neptun) == neptun_size-1)
{
strcpy(this->neptun, neptun);
}
else
{
// cerr << "Bad neptun format." << endl;
throw domain_error("Bad neptun format."); // Ezt csak a félév vége felé kell tudni, addig elég a felső sor
}
}
void Student::setMark(unsigned mark)
{
if(mark > 0 && mark <=5)
{
this->mark = mark;
}
else
{
// cerr << "Bad mark format." << endl;
throw domain_error("Bad mark format.");// Ezt csak a félév vége felé kell tudni, addig elég a felső sor
}
}
std::istream& Student::readBinary(std::istream& is)
{
// Figyeljük meg a "mindent vagy semmit" megközelítést:
// csak akkor változtatjuk az objektum állapotát, ha minden sikerült.
// Addig ideiglenes változókba olvasunk.
// Beolvassuk a sztring hosszát
string::size_type length;
is.read((char*) &length, sizeof(length));
if(!is.good())
return is;
// Beolvassuk a sztringet
char * nameBuffer = new char [length];
is.read(nameBuffer, length);
string name (nameBuffer, length);
delete[] nameBuffer;
// Beolvassuk a neptun kódot
char neptun[neptun_size];
is.read(neptun, neptun_size-1);
neptun[neptun_size-1]='\0';
// Beolvassuk az érdemjegyet
unsigned mark;
is.read((char*)&mark, sizeof(mark));
if(mark == 0 || mark > 5)
is.setstate(ios::failbit);
// Csak akkor változtatjuk meg az objektum állapotát, ha a beolvasás sikeres volt.
// Így tartjuk fenn a konzisztenciát.
if(is)
{
this->name = name;
strcpy(this->neptun, neptun);
this->mark = mark;
}
return is;
}
void Student::writeBinary(std::ostream& os)const
{
// Először kiírjuk, hány karakter van, utána pedig a karakterek következnek
string::size_type length = name.length();
os.write((const char*)&length, sizeof(length));
os.write(name.data(), length);
// Neptun kiírása a nulla nélkül (ennek hossza mindig ugyanannyi, ezért nem írjuk ki
os.write(neptun, neptun_size-1);
// Kiírjuk a jegyet is
os.write((const char *)&mark, sizeof(mark));
}
std::istream& Student::readText(std::istream& is)
{
// Változók az ideiglenes adatoknak: csak akkor változtatjuk az objektum értékét, ha minden
// beolvasás sikeres volt.
string name;
char neptun[neptun_size];
unsigned mark;
getline(is,name);
is.width(neptun_size);
is >> neptun;
if(strlen(neptun) != neptun_size-1)
is.setstate(ios::failbit);
is >> mark;
if(mark == 0 || mark > 5)
is.setstate(ios::failbit);
if(is) // Ha nem volt hiba
{
this->name = name;
strcpy(this->neptun, neptun);
this->mark = mark;
}
return is;
}
void Student::writeText(std::ostream& os)const
{
os << name << endl << neptun << endl << mark;
}
A tesztkód:
#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;
int main()
{
Student s("James Bond", "npt007", 5);
try
{
// Kiírás tesztelése
s.writeText(cout); cout << endl;
// Beolvasás tesztelése
cout << "Enter the name, neptun and mark, separated with <Enter> for a student: ";
s.readText(cin);
if(cin)
{
s.writeText(cout);
cout << endl;
}
else
{
cerr << "Error reading student." << endl;
}
// Tesztelés állományokkal
// (a destruktor lezárja az állományt, csak akkor zárjuk le a close tagfüggvénnyel,
// ha az állományt megakarjuk nyitni.)
// 1. Kiírás bináris állományba
ofstream outFileBinary("student.bin", ios::out|ios::binary);
s.writeBinary(outFileBinary);
outFileBinary.close();
// 2. Beolvasás bináris állományból
ifstream inFileBinary ("student.bin", ios::in|ios::binary);
Student s2;
s2.readBinary(inFileBinary);
if(inFileBinary)
{
s2.writeText(cout); cout << endl;
}
else
{
cerr << "Error reading student." << endl;
}
// 3. Kiírás szöveges állományba
ofstream outFileText("student.txt", ios::out);
s.writeText(outFileText);
outFileText.close();
// 4. Beolvasás szöveges állományból
ifstream inFileText("student.txt", ios::in);
s2.readText(inFileText);
if(inFileText)
{
s2.writeText(cout); cout << endl;
}
else
{
cerr << "Error reading student." << endl;
}
// 1. Több hallgató kiírása bináris állományba
ofstream outFileBinaryStudents("students.bin", ios::out|ios::binary);
for(int i=0; i<=5; i++)
{
s.writeBinary(outFileBinaryStudents);
}
outFileBinaryStudents.close();
// 2. Több hallgató beolvasás bináris állományból
ifstream inFileBinaryStudents ("students.bin", ios::in|ios::binary);
// Itt kényelmes a visszatérési érték.
while(s2.readBinary(inFileBinaryStudents))
{
s2.writeText(cout); cout << endl;
}
// 3. Több hallgató kiírása szöveges állományba
ofstream outFileTextStudents("students.txt", ios::out);
for(int i=0; i<=3;i++)
{
s.writeText(outFileTextStudents);
}
outFileText.close();
// 4. Több hallgató beolvasása szöveges állományból
ifstream inFileTextStudents("students.txt", ios::in);
// Itt kényelmes a visszatérési érték.
while(s2.readText(inFileTextStudents))
{
s2.writeText(cout); cout << endl;
}
}
catch(const exception& e) // Elkapjuk a kivételeket
{
cout << e.what() << endl;
}
}