W przypadku zapisu danych składających się często z rekordów o zmiennej długości wygodnie jest wczytywać i zapisywać dane jako tekst, gdzie kolejne elementy danego rekordu są oddzielone umownym znakiem. Takim umownym znakiem może być np. tabulator, średnik i w zasadzie każdy znak, który nie będzie kolidował z formatem zapisywanych danych. W celu skorzystania z klasy odpowiedzialnej za otwieranie i zapis danych w pliku konieczne jest załączenie pliku nagłówkowego fstream, w którym to znajduje się deklaracja klasy o tej samej nazwie, czyli fstream, która to zostanie wykorzystana do zapisu i odczytu danych w pliku tekstowym.
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
int main(){
fstream file("file.txt", // ścieżka do otwieranego pliku (w tym przypadku względna)
std::ios_base::out // tryb otwarcia do zapisu
); // utworznie obiektu typu fstream
if(file.is_open()){ // metoda is_open() sprawdza, czy plik rzeczywiście został otwarty (równie dobrze można użyć metody .good())
for(int i = 0; i < 10; i++){ // od 0 do 9
file << "Tekst "<<i + 1<<endl; // zapis tekstu z znakiem nowej linii na końcu
}
file.close(); // zamykanie pliku
}
file.open("file.txt",std::ios_base::in); // otwarcie tego samego pliku w trybie do odczytu
if(file.is_open()){ // jeżeli otwarte
char buffor[255]; // bufor danych
while(!file.eof()){ // dopuki nie osiągnięto końca pliku
file.getline(buffor, sizeof(buffor) / sizeof(char)); // wczytywanie linii tekstu z pliku (do znaku końca linii)
cout<<buffor<<endl<<endl; // wyświetlanie tekstu
}
file.close(); // zamykaj plik
}
file.open("file.txt",std::ios_base::in); // otwarcie tego samego pliku w trybie do odczytu
if(file.is_open()){ // jeżeli otwarte
string str; // bufor danych
while(!file.eof()){ // dopuki nie osiągnięto końca pliku
getline(file, str); // wczytywanie linii tesktu z pliku (do znaku końca linii) do zmiennej typu string
cout<<str<<endl<<endl; // wyświetlanie tekstu
}
file.close(); // zamykaj plik
}
cout<<"Wcisnij enter, aby zamknac program...";
cin.get();
return 0;
}
Jak wynika z powyższego kodu, już przy tworzeniu obiektu klasy fstream można otworzyć plik, podając konstruktorowi tejże klasy: względną (np. "file.txt") lub bezwzględną ścieżkę dostępu do pliku (np. "C:\file.txt"). Kolejny argument określa tryb dostępu do pliku, który może być kombinacją dwóch ustawień: std::ios::in - do odczytu; std::ios::out - do zapisu. Aby otworzyć plik do odczytu i do zapisu trzeba podać argument: std::ios::in|std::iso::out. Istnieje też możliwość otworzenia pliku za pomocą wewnętrznej metody open, która przyjmuje te same argumenty. Poniżej zamieszczam opis możliwych do użycia trybów odczytu pliku:
Po otworzeniu pliku trzeba się upewnić, czy aby na pewno ten został otworzony. Do tego celu można posłużyć się jedną z dwóch dostępnych metod: is_open() lub good(), obie te metody zwracają true, gdy otwarcie pliku się powiodło, zaś false w przeciwnym przypadku. Równie ważna jest metoda eof(), która zwraca true, gdy podczas odczytu osiągnięto koniec pliku.
W powyższym przykładzie dwukrotnie otworzyłem ten sam plik. W pierwszym jednak przypadku użyłem metody wewnętrznej getline(buffer, bufferSize) gdzie buffer jest typu const char*, a bufferSize jest typu unsigned int; natomiast w drugim funkcji std::getline(file, str) gdzie z kolei pierwszy argument jest typu fstream, drugi zaś typu string.
void ParseString(std::string str_to_parse, std::vector<std::string> &tString,std::string parse_signs){
if(tString.size()){
tString.clear();
}
std::string::size_type sizeBegin = 0;
std::string::size_type sizeEnd = str_to_parse.find_first_of(parse_signs, sizeBegin);
while(sizeEnd != std::string::npos){
if(sizeEnd != sizeBegin){
tString.push_back(
str_to_parse.substr(sizeBegin, sizeEnd - sizeBegin)
);
}
tString.push_back(
str_to_parse.substr(sizeEnd,1)
);
sizeBegin = sizeEnd + 1;
sizeEnd = str_to_parse.find_first_of(parse_signs, sizeBegin);
};
if(sizeBegin < str_to_parse.size()){
tString.push_back(
str_to_parse.substr(sizeBegin)
);
}
}
void SaveToFile(std::vector<iObject*> &tObject){ // funkcja zapisująca dane o utworzonych obiektach do pliku tekstowego
std::fstream file("SaveFile.txt",std::ios_base::out); // otwieranie pliku do zapisu
if(file.is_open()){ // czy otwarty
for(std::vector<iObject*>::iterator i = tObject.begin(); i < tObject.end(); i++){ // iterowanie po interfejsach obiektów zawartych w kontenerze
(*i)->WriteToFile(file); // zapis
}
}else{
std::cout<<"Wzielo sie i nie zapisalo"<<std::endl<<std::endl;
}
}
void ReadFromFile(std::vector<iObject*> &tObject){ // czytanie danych z pliku tekstowego
std::fstream file("SaveFile.txt",std::ios_base::in); // otwieranie pliku
if(file.is_open()){ // sprawdzanie, czy się otworzył
while(!file.eof()){ // czy koniec pliku został osiągnięty, jak nie to
std::string temp; // pomocnicza do wczytywania zmienna
std::getline(file, temp); // wczytuję
std::vector<std::string> tStr; // tablica do parsowania tekstu z zmiennej pomocniczej temp
ParseString(temp, tStr, " ;="); // parsuję tekst względem spacjji, średnika i znaku równości
if(tStr.size()){ // jak tStr.size() nie jest równe zero to
if(tStr[0].compare("Point2d:") == 0){ // jak pierwszy element zawiera tekst "Point2d:" to
if(tStr.size() == 11){ // jak liczba elementów w tablicy sparsowanego tekstu się zgadza to
std::cout<<"Wczytano Point2d "<<tStr.size()<<std::endl; // info, że tak, syćko jest w pożądku
tObject.push_back(new point2d(atol(tStr[4].c_str()), atol(tStr[9].c_str()))); // i dodaję do tablicy interfejsów nowy obiekt
}else{ // a jak nie, to znaczy się, że ktoś spartolił formatowanie i się nie wczyta
std::cout<<"Cos nie tak w zapisie \apos"<<temp<<std::endl<<std::endl;
}
}else if(tStr[0].compare("Point3d:") == 0){ // to samo tylko dla "Point3d:"
if(tStr.size() == 16){
std::cout<<"Wczytano Point3d "<<tStr.size()<<std::endl;
tObject.push_back(new point3d(atol(tStr[2].c_str()), atol(tStr[4].c_str()), atol(tStr[14].c_str())));
}else{
std::cout<<"Cos nie tak w zapisie \apos"<<temp<<std::endl<<std::endl;
}
}else if(tStr[0].compare("Line:") == 0){ // to samo tylko dla "Line:"
if(tStr.size() == 21){
std::cout<<"Wczytano Line "<<tStr.size()<<std::endl;
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
sscanf(temp.c_str(),"Line: x1=%i; y2=%i; x2=%i; y2=%i;",&x1, &y1, &x2, &y2);
tObject.push_back(new line(x1, y1, x2, y2));
}else{
std::cout<<"Cos nie tak w zapisie \apos"<<temp<<std::endl<<std::endl;
}
}else if(tStr[0].compare("Circle:") == 0){ // to samo tylko dla "Circle:"
if(tStr.size() == 16){
std::cout<<"Wczytano Circle "<<tStr.size()<<std::endl;
int xc = 0, yc = 0, ray = 0;
sscanf(temp.c_str(),"Circle: xc=%i; yc=%i; ray=%i;",&xc, &yc, &ray);
tObject.push_back(new circle(xc, yc, ray));
}else{
std::cout<<"Cos nie tak w zapisie \apos"<<temp<<std::endl<<std::endl;
}
}else if(tStr[0].compare("Ellipse:") == 0){ // to samo tylko dla "Ellipse:"
if(tStr.size() == 21){
std::cout<<"Wczytano Ellipse "<<tStr.size()<<std::endl;
int xc = 0, yc = 0, r1 = 0, r2 = 0;
sscanf(temp.c_str(),"Ellipse: xc=%i; yc=%i; r1=%i; r2=%i",&xc, &yc, &r1, &r2);
tObject.push_back(new ellipse(xc, yc, r1, r2));
}else{
std::cout<<"Cos nie tak w zapisie \apos"<<temp<<std::endl<<std::endl;
}
}
}
}
}else{
std::cout<<"Wzielo sie i nie wczytalo"<<std::endl<<std::endl;
}
}
Oraz zmienione nieco instrukcje wewnątrz funkcji głównej programu:
int main(){
setlocale(LC_CTYPE,"Polish"); // to po to, żeby mieć polskie znaki
std::vector<iObject*> tObject;
ReadFromFile(tObject); // wczytywanie obiektów na podstawie danych zapisanych w pliku tekstowym
WhatYouWantToDo(tObject);
SaveToFile(tObject); // zapisywanie obiektów w pliku tekstowym
DeleteAllObject(tObject,false);
return 0;
}
Zapis/odczyt danych binarnych
Gdy istnieje konieczność zapisania dużej liczby danych stosuje się nieco inny sposób wczytywania. Po pierwsze potrzebna będzie struktura, która będzie nagłówkiem tworzonego pliku. W nagłówku powinny zostać zawarte informacje dotyczące rozmiaru wczytywanych danych. Oto prościutki przykład programu, który zapisuje dane do pliku i wczytuje dane z pliku:
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
struct headerfile{ // a to będzie struktura nagłówkowa
unsigned int sizedata; // rozmiar danych zawartych w pliku zaraz za nagłówkiem
char text[21]; // jakiś tam dodatkowy tekst w nagłówku
char author_name[21]; // autor pliku
};
int main(){
fstream file("file.txt",std::ios_base::out|std::ios::binary); // otwieranie do odczytu
if(file.is_open()){ // jak otwarty
cout<<"otworzylem"<<endl;
string data = "dane do zapisu w pliku binarnym";
headerfile *hf = new headerfile; // deklaruję pamięć nagłówka
hf->sizedata = data.size() + 1; // zapisuję informację o rozmiarze danych
cout<<data.size() + 1<<endl<<endl;
strcpy(hf->text, "Jakis tekst"); // wczytuje tekst do pola nagłówka text
strcpy(hf->author_name, "Programista"); // to samo dla pola autor_name
file.write((char*)hf, sizeof(headerfile)); // tutaj zapisuję nagłówek w pliku
file.write(data.c_str(), data.size()); // tutaj zapisuję dane dodatkowe w pliku
delete hf; // zwalniam przydzieloną pamięć
hf = NULL;
file.close(); // zamykanie pliku
}
file.open("file.txt",std::ios_base::in|std::ios::binary); // otwieranie do odczytu
if(file.is_open()){ // jak otwarty
cout<<"otworzylem"<<endl;
char* bf = new char[sizeof(headerfile)]; // przygotowuję bufor danych dla nagłówka
file.read(bf, sizeof(headerfile)); // czytam nagłówek
headerfile *hf = (headerfile*)bf; // uzyskuję dane nagłówka
char* data = NULL; // tutaj będę przechwytywał dane dodatkowe z pliku
string datastr; // a tu je zapiszę jako string
if(hf->sizedata){ // jeżeli sizedata nie równe zero to
data = new char[hf->sizedata]; // rezerwowanie pamięci potrzebnej do pozyskania danych
file.read(data, hf->sizedata); // wczytanie danych
data[hf->sizedata - 1] = 0; // na końcu zero powinno być
datastr = data; // przepisanie danych do stringa
delete data; // zwolnienie pamięci
}
cout<<hf->sizedata<<" "<<hf->text<<" "<<hf->author_name<<" "<<datastr<<endl<<endl; // wypisanie na ekranie wczytanych danych
delete bf; // zwalnianie pamięci bufora
hf = NULL; // zerowanie wskaźnika nagłówka
bf = NULL; // zerowanie wskaźnika bufora
file.close(); // zamykanie pliku
}
cout<<"Wcisnij enter, aby zaknac program...";
cin.get();
return 0;
}