Operacje we/wy na plikach
Stronę tą wyświetlono już: 4772 razy
Zapis/odczyt danych tekstowych
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:
std::ios_base::app | otwieranie z dopisywaniem danych do końca pliku; |
std::ios_base::ate | ustawia wskaźnik pliku na jego koniec; |
std::ios_base::binary | wczytywanie danych binarnych; |
std::ios_base::in | tryb do odczytu danych; |
std::ios_base::out | tryb zapisu danych do pliku; |
std::ios_base::trunc | zrzuca zawartość pliku, która jest tracona w chwili jego otwierania. |
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.
Czas zrobić nieco ciekawszy przykład, na bazie programu, który wykonałem na stronie Programowanie → Podstawy C++ → Metody wirtualne, czysto wirtualne oraz klasy abstrakcyjne, nie będę przepisywał całego projektu, powiem tylko co gdzie należy dodać. Do definicji klasy abstrakcyjnej iObject należy dodać taką oto czysto wirtualną metodę:
- virtual void WriteToFile(std::fstream &f) const =0;// metoda czysto wirtualna związana z obsługą zapisu danych do pliku
Do definicji klasy point2d metodę:
- inline virtual void WriteToFile(std::fstream &f) const {f <<"Point2d: x="<<x<<"; y="<<y<<";"<<std::endl;}; // obsługa zapisu do pliku
Do definicji klasy point3d metodę:
- inline virtual void WriteToFile(std::fstream &f) const {f <<"Point3d: x="<<x<<"; y="<<y<<"; z="<<z<<";"<<std::endl;}; // obsługa zapisu do pliku
Do definicji klasy line metodę:
- inline virtual void WriteToFile(std::fstream &f) const {f <<"Line: x1="<<firstPoint.GetX()<<"; y2="<<firstPoint.GetY()<<"; x2="<<secondPoint.GetX()<<"; y2="<<secondPoint.GetY()<<";"<<std::endl;}; // obsługa zapisu do pliku
Do definicji klasy circle metodę:
- inline virtual void WriteToFile(std::fstream &f) const {f <<"Circle: xc="<<x<<"; yc="<<y<<"; ray="<<ray<<";"<<std::endl;}; // obsługa zapisu do pliku
De definicji klasy ellipse metodę:
- inline virtual void WriteToFile(std::fstream &f) const {f <<"Ellipse: xc="<<x<<"; yc="<<y<<"; r1="<<r1<<"; r2="<<r2<<";"<<std::endl;}; // obsługa zapisu do pliku
Na koniec kilka funkcji dodatkowych:
- 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 \""<<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 \""<<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 \""<<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 \""<<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 \""<<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;
- }

Tytuł:
Architektura oprogramowania bez tajemnic. Wykorzystaj język C++ do tworzenia wydajnych aplikacji i systemów
Autor:
Adrian Ostrowski, Piotr Gaczkowski

Tytuł:
Opus magnum C++ 11. Programowanie w języku C++. Wydanie II poprawione (komplet)
Autor:
Jerzy Grębosz

Tytuł:
Programowanie wieloplatformowe z C++ i wxWidgets 3
Autor:
Bartosz W. Warzocha

Tytuł:
Język C++ i przetwarzanie współbieżne w akcji. Wydanie II
Autor:
Anthony Williams

Tytuł:
C++ dla bystrzaków. Wydanie VII
Autor:
Stephen R. Davis

Tytuł:
Tablice informatyczne. Podstawy C++
Autor:
Radosław Sokół

Tytuł:
Opus magnum C++11. Programowanie w języku C++ (komplet)
Autor:
Jerzy Grębosz

Tytuł:
OpenCV 3. Komputerowe rozpoznawanie obrazu w C++ przy użyciu biblioteki OpenCV
Autor:
Adrian Kaehler, Gary Bradski

Tytuł:
C++ w 24 godziny. Wydanie VI
Autor:
Rogers Cadenhead, Jesse Liberty

Tytuł:
C++ Optymalizacja kodu. Sprawdzone techniki zwiększania wydajności
Autor:
Guntheroth Kurt