Pliki wykład Dorota Pylak
Pliki 2 Większość programów komputerowych korzysta z plików, np. w edytorach tekstów tworzymy pliki dokumentów. Plik (ang. file) jest porcją danych zapisanych na jakimś nośniku. Pliki mogą zawierać dane z których chcemy korzystać w naszym programie - pliki danych lub możemy w nich zapisywać rezultaty - pliki wynikowe. Dzięki wykorzystaniu plików nie musimy wprowadzać wielokrotnie danych, a raz wprowadzone np. przy pomocy jakiegoś edytora używać wiele razy. Pliki wejścia/wyjścia mogą być otwierane w trybie tekstowym lub w trybie binarnym. Np. w formacie tekstowym, liczby są zapisywane, jako łańcuchy znaków, natomiast w formacie binarnym liczby są zapisywane tak jak w pamięci: cztery bajty na liczbę całkowitą (int) i osiem na liczbę zmiennoprzecinkową (double). Format tekstowy jest bardziej czytelny, pliki takie można edytować w zwykłych edytorach tekstu, natomiast format binarny zapewnia dokładniejszy zapis liczb, ponieważ zapisywana jest dokładna wewnętrzna reprezentacja liczby. Do przetwarzania plików w języku C++ stosowane są strumienie.
Strumienie Operacje we/wy realizuje się w C++ za pomocą strumieni. Strumień możemy wyobrażać sobie jako ciąg danych (informacji), w postaci bajtów, płynący od źródła do ujścia. Zachodzą przy tym dwie możliwości: informacja płynie od naszego programu do odbiornika (ujścia). Odbiornikiem może być terminal, plik, gniazdo sieciowe, obszar w pamięci, nazwany potok itd. O takim strumieniu mówimy, że jest strumieniem wyjściowym (ang. output stream), informacja płynie do naszego programu ze źródła. Źródłem może być terminal, plik, itd. O takim strumieniu mówimy, że jest strumieniem wejściowym (ang. input stream). Strumień służy do zapisywania bądź odczytywania informacji - dowolnych danych
Strumienie- uproszczony schemat klas 4 Do reprezentowania strumieni i obsługi operacji na nich zdefiniowane są specjalne klasy wyposażone w pola i metody.
Strumienie- podstawowe klasy 5 Podstawową dla nas klasą jest klasa ios, po niej dziedziczą klasy: istream podstawowa klasa reprezentująca strumienie wejściowe. W niej, między innymi, zdefiniowane jest przeciążenie operatora '>>' i tej właśnie klasy obiektem jest cin, reprezentujący standardowy strumień wejściowy. Bardziej wyspecjalizowane klasy do obsługi strumieni wejściowych to m.in: istringstream źródłem jest obiekt klasy string, czyli napis. Dostępna po dołączeniu za pomocą dyrektywy #include<sstream>. ifstream (z ang. intput file stream) źródłem jest plik. Udostępniana po dołączeniu fstream.
Strumienie- podstawowe klasy 6 ostream podstawowa klasa reprezentująca strumienie wyjściowe. W niej właśnie zdefiniowane jest przeciążenie operatora '<<' i obiekty cout, reprezentujący standardowy strumień wyjściowy oraz cerr i clog, reprezentujące standardowy strumień błędów niebuforowany i buforowany, a także manipulator endl. Bardziej wyspecjalizowane klasy do obsługi strumieni wyjściowych to m.in.: ostringstream ujściem jest obiekt klasy string, czyli napis. Udostępniana po dołączeniu pliku sstream. ofstream (z ang. output file stream) odbiornikem jest plik. Udostępniana po dołączeniu pliku nagłówkowego fstream.
Strumienie- podstawowe klasy 7 Klasa iostream dziedziczy zarówno po istream, jak i po ostream, zatem dołączając iostream zapewniamy funkcjonalność obu. Podobnie, dołączając fstream zapewniamy dostęp do klas ifstream i ofstream, a dołączając sstream do istringstream i ostringstream. Podsumowując: jeśli w programie wykonujemy tylko konsolowe operacje we/wy (pisanie na ekran, czytanie z klawiatury), to wystarczy dołączyć plik nagłówkowy iostream; jeśli wykonujemy operacje na plikach, to trzeba włączyć plik nagłówkowy fstream; jeśli wykonujemy operacje we/wy, dla których źródłem lub ujściem są obiekty klasy string, to należy dołączyć sstream.
Strumienie- podstawowe klasy 8 Struktura programu działającego na strumieniach: kojarzy strumień z zewnętrznym źródłem/odbiornikiem, otwiera strumień, dodaje lub pobiera dane ze strumienia, zamyka strumień.
Pliki program zapisujący dane do pliku 9 Struktura programu, który zapisuje dane do pliku: 1) Dyrektywa preprocesora #include <fstream> //zapewnia dostęp do klas ifstream // i ofstream 2) deklaracja zmiennej (strumienia) typu ofstream ofstream plikwy; 3) wywołanie funkcji wiążącej strumień z konkretnym plikiem i otwierającej go plikwy.open("wyniki.txt"); Kroki 2) i 3) tj. utworzenie obiektu i skojarzenie go z plikiem możemy też zrealizować za pomocą jednej instrukcji: ofstream plikwy("wyniki.txt"); //2)3) drugi sposób
Pliki program zapisujący dane do pliku 10 4) działania na plikach: możemy teraz używać strumienia plikwy w taki sam sposób jak obiektu cout, (wstawiamy nazwę zmiennej strumieniowej typu ofstream w miejsce cout)np. plikwy << "jakieś dane do zapisu" << endl int n = 5; plikwy << n; << "kolejne dane"; //możemy korzystać z manipulatorów //pamiętajmy aby dołączyć dyrektywę: //#include<iomanip> double s = 10.578; plikwy <<fixed << setprecision(2) << x << endl;
11 Pliki program zapisujący dane do pliku 5) zamknięcie połączenia z plikiem plikwy.close();//obiekt plikwy istnieje nadal i możemy //go użyć do połączenia z jakimś plikiem Uwaga 1: Otwarcie w ten sposób pliku do zapisu spowoduje utworzenie nowego pliku, jeśli nie ma jeszcze pliku o tej nazwie, a jeśli plik o tej nazwie istnieje jego zawartość zostanie skasowana i operacje wyjścia rozpoczną zapis do pustego pliku.
12 Pliki program zapisujący dane do pliku - uwagi Uwaga 2: Ścieżki do pliku mogą być względne oraz bezwzględne. Ścieżka względna to taka, która nie zawiera pełnej ścieżki do pliku, np. sama nazwa pliku wraz z rozszerzeniem, bądź ścieżka względem katalogu roboczego aplikacji. Ścieżka bezwzględna określa natomiast pełną ścieżkę do pliku i zaczyna się od nazwy dysku, a kończy się na pełnej nazwie pliku np. "D:\\plik.txt" lub "D:/mojkatalog/plik.txt". Uwaga 3. Nazwa pliku w funkcji open jak i w konstruktorze jest C- napisem. Dlatego jeśli jest ona pobierana do zmiennej string musimy przekonwertować ją do C-napisu przy pomocy metody c_str() : string snazwapliku; cout << "Podaj nazwę pliku: "; cin >> snazwapliku; ofstream plik; plik.open( snazwapliku.c_str() ); //C++ 98 od C++11 istnieje druga wersja funkcji open, z parametrem typu string plik.open( snazwapliku ); //C++ 11
Pliki program odczytujący dane z pliku 13 Operacje niezbędne do realizacji odczytu z pliku są podobne do operacji zapisu do pliku 1) dołączamy plik nagłówkowy fstream #include <fstream> //zapewnia dostęp do klas // ifstream i ofstream 2) deklarujemy zmienną (strumienia) typu ifstream ifstream plikwe; 3) kojarzymy strumień z konkretnym plikiem i otwieramy go plikwe.open("dane.txt"); Kroki 2) i 3) tj. utworzenie obiektu i skojarzenie go z plikiem możemy też zrealizować za pomocą jednej instrukcji: ifstream plikwe("dane.txt"); //2)3) drugi sposób
Pliki program odczytujący dane z pliku 14 4) działania na plikach: możemy teraz używać strumienia plikwe w taki sam sposób jak strumienia cin (operator >> pomija białe znaki), np. dla pliku zawierającego liczby całkowite oddzielone znakami białymi (spacje, tabulacje, znak nowej linii) int x; //dopóki czytanie z pliku się powodzi while (plikwe>>x) { } //tj. nie napotkano końca pliku lub błędnych danych //jakieś operacje na x
Pliki program odczytujący dane z pliku 15 Uwaga 1. Operator >> zawsze pomija wszelkie napotkane białe znaki Uwaga 2. Operator '<<' lub '>>' jest funkcją, która prócz wykonania samej operacji we/wy, zwraca jeśli operacja się powiodła referencję do tego samego obiektu-strumienia, który był jej pierwszym (lewym) argumentem 5) zamykamy połączenie strumienia z plikiem plikwe.close(); //obiekt plikwe istnieje nadal //i możemy go użyć //do połączenia z innym //(lub ponownie z tym samym) plikiem
Kontrola strumienia i metoda is_open() 16 Stan strumienia sprawdzamy, aby dowiedzieć się czy ostatnia operacja powiodła się, np. czy powiodło się otwarcie pliku (plik nie istnieje, nie posiadamy uprawnień do odczytu/modyfikacji), napotkano koniec pliku lub błędne dane. Po wykonaniu operacji, wewnątrz obiektu ustawiane są odpowiednie flagi (znaczniki), które informują o tym, czy się ona powiodła, czy też nie. Funkcje sprawdzające stan strumienia to m.in.: good() i fail(): ifstream fin; fin.open("nazwapliku.txt"); if (fin.fail()) //proba otwarcia zakończyła się // niepowodzeniem //lub if (!fin.good()) //otwarcie nie powiodło się
Kontrola strumienia i metoda is_open() 17 Ponieważ obiekt fin podlega konwersji na bool, to stan strumienia możemy sprawdzić poprzez lub if(fin) //jeśli jest ok if(!fin) //jeśli plik jest w stanie błędu-awarii Przy czym do sprawdzania czy otwarcie pliku nie powiodło się zaleca się korzystanie z funkcji is_open(): if(!fin.is_open()) //otwarcie nie powiodło się gdyż wykrywa ona wszystkie błędy wykrywane przez good(), a ponadto wykrywa próby otwarcia pliku z zastosowaniem nieodpowiedniego trybu otwarcia pliku.
Pliki pętla odczytująca dane z pliku 2 sposób 18 drugi sposób organizacji pętli pobierającej dane z pliku (sprawdzamy stan strumienia) int x; fin>>x; //odczyt pierwszej danej z pliku przed pętlą //jeśli napotkano znak końca pliku lub błędne dane //plik wchodzi w stan błędu (awarii) while (fin)//dopóki czytanie z pliku się powodzi { //jakieś operacje na x fin>>x; //odczyt kolejnej danej z pliku }
19 Przykład 1 W pliku "mapa.txt" (znajdującym się w tym samym katalogu co pisany program) znajdują się następujące dane: skala odl_1 odl_2... odl_n. Skala określa jaka odległość w kilometrach odpowiada jednemu milimetrowi na mapie. Napisz program, który obliczy długość trasy w terenie na podstawie odczytów z mapy w danej skali. Otrzymany wynik zapisze w pliku "trasa.txt". Przed podaniem łącznej długości trasy, w pliku tym zapisze w osobnych liniach numery oraz długości poszczególnych odcinków trasy w km. Dane poprzedzić odpowiednimi nagłówkami. Np. dla pliku "mapa.txt" zawierającego dane: 2 1.6 3.3 3.75 4.2 1.4 plik " trasa.txt" ma wyglądać następująco: Odcinek długość 1 3.2 2 6.6 3 7.5 4 8.4 5 2.8 RAZEM: 28.5 km
20 #include <iostream> #include <iomanip> #include <fstream> using namespace std; int main() { Przykład 1 ifstream pwe; //deklaracja strumienia wejściowego pwe.open("mapa.txt");//kojarzymy z plikiem i otwieramy if(!pwe.is_open()) //jeśli błąd otwarcia pliku { //konczymy program } cout<<"blad otwarcia pliku"<<endl; return 1;//wychodzimy z bledem
Przykład 1 21 ofstream pwy; //tworzymy strumień wyjściowy pwy.open("trasa.txt"); //kojarzymy z plikiem //i otwieramy if(!pwy.is_open()) { //kończymy program cout<<"blad otwarcia pliku wynikowego"<<endl; pwe.close(); //zamykamy otwarty strumien return 1; }
Przykład 1 pwy << "Odcinek dlugosc\n"; //wstawiamy tekst nagłówka pwy << fixed << setprecision(1);//formatowanie wyników float skala, odlmm, //odległość z mapy w mm odlkm, //odległość przeliczona w kilometrach trasa = 0.0f; //łączna długość trasy int nr = 0; //zmienna do numeracji kolejnych odcinków pwe >> skala; //wczytanie skali z pliku wejściowego if (!pwe) //nie powiodło się czytanie, plik pusty lub blad { pwe.close(); pwy.close(); //zamykamy strumienie cout<<"plik pusty lub blad"<<endl; return 1; //kończymy program z błędem } 22
Przykład 1 23 //dopóki udaje się czytać z pliku while (pwe >> odlmm) { //pobraliśmy kolejny odcinek nr++ ; odlkm = odlmm*skala ; //przeliczamy odleglosc //zapisujemy odcinek do pliku wynikowego pwy << setw(6) << nr << setw(8) << odlkm << endl; //i dodajemy do ogólnej długości trasy trasa = trasa + odlkm; }
Przykład 1 24 // odczytaliśmy wszystkie dane z pliku "mapa.txt" // należy dopisać ostatnią linię z podsumowaniem pwy << "RAZEM:" << setw (8 ) << trasa << " km"; pwe.close(); pwy.close(); return 0; }
Przykład 2: pliki i funkcje 25 Dany jest plik tekstowy liczbyr.txt zawierający liczby rzeczywiste oddzielone znakami białymi (tabulatory, spacje, znaki końca linii). Napisz dwie wersje funkcji logicznej SumaPliku, które obliczają sumę elementów pliku: dla pierwszej funkcji plik podany jest przez nazwę pliku, a druga funkcja otrzymuje plik jako strumień gotowy do pracy. Wartością funkcji ma być true w przypadku, gdy wszystkie operacje na pliku powiodły się i false w przeciwnym wypadku. Jeśli plik był pusty funkcja zwraca false. Obliczona suma przekazywana ma być przez parametr.
#include<iostream> #include<fstream> #include<string> Przykład 2 (funkcja dostaje nazwę pliku) using namespace std; //funkcja oblicza sumę liczb w pliku bool SumaPlik(string nazwa, double &suma) { ifstream pwe; pwe.open(nazwa); if(!pwe.is_open()) return false;//nie udało się otworzyć strumienia suma = 0.0; // zmienna na sumę double x; // zmienna do której będziemy pobierać // kolejne liczby z pliku 26
Przykład 2 (funkcja dostaje nazwę pliku) if (!(pwe>>suma)) { //jeśli pobranie 1-ej liczby nie powiodło się //plik pusty lub błąd } pwe.close(); return false; //dopoki się udaje pobierac kolejne liczby while( pwe >> x ) //pobieramy kolejne elementy z pliku suma += x; //dodajemy do sumy 27
Przykład 2 (funkcja dostaje nazwę pliku) 28 } //w naszych programach będziemy zakładać, że pliki mają //poprawną strukturę, ale jeśli by tak nie było //to możemy sprawdzić, czy osiągnęliśmy koniec pliku //czy był błąd, pwe>>x nie powiodło się //(plik jest w stanie błędu) if(!pwe.eof()) {//jeśli nie doszliśmy do końca pliku pwe.close(); return false; } pwe.close(); return true;
Przykład 2 (funkcja dostaje nazwę pliku) int main() { //funkcja dostaje nazwę pliku string npliku; cout << "podaj nazwe pliku "; cin >> npliku; double sum; } if( SumaPlik(npliku,sum) ) cout << "suma liczb w pliku = " << sum << endl; else cout << "blad operacji plikowych" << endl; return 0; 29
Przykład 2 (funkcja dostaje referencję na strumień) //funkcja oblicza sumę liczb w pliku, jako parametr dostaje // strumień gotowy do użycia bool SumaPlik(ifstream &pwe, double &suma) { if(!pwe) return false; suma = 0.0; double x; if (!(pwe>>suma)){ //czytanie 1-go elem. się nie udało } pwe.close(); return false; //plik pusty lub błąd while(pwe >> x) suma += x; } return true; 30
Przykład 2 (funkcja dostaje referencję na strumień) 31 int main() { //funkcja dostaje & na strumień string nazwa; cout << "podaj nazwe pliku: "; cin >> nazwa; double s; ifstream pwe(nazwa.c_str()); //ifstream pwe(nazwa); //w C++11 if(sumaplik(pwe,s)) cout<<"suma liczb w pliku = "<<s<<endl; else cout<<"blad operacji plikowych"<<endl; pwe.close(); return 0; }