Pliki wykład 2 Dorota Pylak
Struktura programu działającego na plikach 1) Dyrektywa preprocesora #include <fstream> //zapewnia dostęp do strumieni ifstream i ofstream 2) deklaracja zmiennej (strumienia) typu wyjściowego ofstream (w przypadku zapisu do pliku) ofstream plik; lub wejściowego ifstream (gdy chcemy czytać z pliku) ifstream plik; 3) wywołanie funkcji wiążącej strumień z konkretnym plikiem i otwierającej go plik.open("nazwa_pliku.txt"); Uwaga 1: Kroki 2) i 3) tj. utworzenie obiektu i skojarzenie go z plikiem możemy też zrealizować za pomocą jednej instrukcji: //lub ofstream plik("nazwa_pliku.txt"); //2)3) drugi sposób ifstream plik("nazwa_pliku.txt"); 2
Struktura programu działającego na plikach 3 Uwaga 2: 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 4) sprawdzenie czy otwarcie się powiodło np. if(!plik.is_open()) //jeśli otwarcie nie powiodło się cout<<"blad otwarcia pliku"; return 1; //kończymy program
Struktura programu działającego na plikach 4 5) działania na plikach: możemy teraz w przypadku pliku do zapisu używać strumienia wyjściowego ofstream plik w taki sam sposób jak obiektu cout (wstawiamy nazwę zmiennej strumieniowej typu ofstream w miejsce cout) np. plik << "jakieś dane do zapisu" << endl << "kolejne dane"; int n = 5; plik << n; //możemy korzystać z manipulatorów //pamiętajmy aby dołączyć dyrektywę: //#include<iomanip> double s = 10.578; plik <<fixed << setprecision(2) << x << endl;
Struktura programu działającego na plikach lub w przypadku pliku do odczytu używać strumienia wejściowego ifstream plik jak obiektu cin np. dla pliku zawierającego liczby całkowite oddzielone znakami białymi (spacje, tabulacje, znak nowej linii) int x; while (plik>>x)//dopóki czytanie z pliku się powodzi //tj. nie napotkano końca pliku lub błędnych danych //jakieś operacje na x 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 obiektustrumienia, który był jej pierwszym (lewym) argumentem 5
Struktura programu działającego na plikach 6 drugi sposób organizacji pętli pobierającej dane z pliku (sprawdzamy stan strumienia) int x; plik>>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 (plik)//dopóki czytanie z pliku się powodzi //jakieś operacje na x plik>>x; //odczyt kolejnej danej z pliku
Struktura programu działającego na plikach Uwaga 1. Ponieważ obiekt plik podlega konwersji na bool, to stan strumienia tj. czy ostatnio wykonywana operacja np. otwieranie pliku czytanie (pobieranie danych) z pliku powiodła się czy też nie: próba otwarcia nieistniejącego pliku napotkano koniec pliku lub błędne dane i plik jest w stanie błędu, możemy sprawdzić poprzez if(plik) //jeśli jest ok lub if(!plik) //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(), gdyż dodatkowo wykrywa próby otwarcia pliku z zastosowaniem nieodpowiedniego trybu otwarcia pliku. if(!plik.is_open()) //jeśli otwarcie pliku nie powiodło się 7
Struktura programu działającego na plikach 8 6) zamknięcie połączenia z plikiem plik.close();//obiekt plik istnieje nadal i możemy go użyć //do połączenia z jakimś plikiem Uwaga. 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
Przykład 2: pliki i funkcje 9 Dany jest plik tekstowy liczbyr.txt zawierający liczby rzeczywiste oddzielone znakami białymi (tabulatory, spacje, znaki końca linii). Napisz funkcję logiczną SumaPliku, która oblicza sumę elementów pliku, którego nazwa jest parametrem funkcji. 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.
Przykład (funkcja dostaje nazwę pliku) 10 #include<iostream> #include<fstream> #include<string> using namespace std; //funkcja oblicza sumę liczb w pliku bool SumaPlik(string nazwa, double &suma) ifstream pwe; pwe.open(nazwa.c_str()); //w C++11: pwe.open(nazwa); if(!pwe.is_open())//błąd otwarcia pliku return false; suma=0.0; //zmienna na sumę, zerujemy double x; //zmienna do której będziemy pobierać kolejne liczby z pliku
11 Przykład (funkcja dostaje nazwę pliku) //próbujemy pobrać pierwszy element, aby sprawdzić czy plik pusty if (!(pwe>>suma))//jeśli pobranie 1-ej liczby nie powiodło się pwe.close(); return false; //plik pusty lub błąd danych while(pwe>>x) //pobieramy kolejne elementy z pliku suma+=x; //dodajemy je kolejno do sumy //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;
12 Przykład (funkcja dostaje nazwę pliku) int main() //funkcja dostaje nazwę pliku string npliku; cout << "podaj nazwe pliku "; cin >> npliku; double sum;//zmienna na sumę if(sumaplik(npliku,sum)) cout << "suma liczb w pliku = " << sum << endl; else cout << "blad operacji plikowych" << endl; return 0;
Odczyt/zapis nieformatowany 13 Do tej pory mówiliśmy o zapisie/odczycie formatowanym informacja czytana lub pisana jest w jakiś sposób interpretowana: opuszczane są białe znaki, dokonuje się przekształceń liczb do napisów w różnych formatach itd. Wczytanie danych ze strumienia bez ich interpretacji i formatowania: istream& get(char& c) czyta jeden bajt; zwraca referencję do strumienia, na rzecz którego została wywołana, nie omija znaków białych Argument typu char jest przesyłany przez referencję; po powrocie jego wartością będzie wczytany znak. Może to być dowolny znak, również znak kontrolny, biały lub zerowy '\0'. Jeśli czytanie nie powiodło się, bo napotkany został koniec pliku, znak c będzie równy EOF, czyli znak, który w danym systemie operacyjnym oznacza koniec danych (Ctrl-Z w Windows, Ctrl-D pod Uniksem/Linuksem). Sam strumień będzie wtedy w stanie błędu. Funkcji tej możemy używać dla strumieni wejściowych: plikowych (ifstream), a także dla strumienia cin, gdy chcemy czytać jeden bajt bez pomijania znaków białych.
Odczyt nieformatowany jednoznakowe funkcje wejścia 14 Np. jeśli fin jest strumieniem wejściowym związanym z plikiem, to można zawartość tego pliku przekopiować na standardowe wyjście prostą pętlą int main() ifstream fin("dane.txt"); if(!fin.is_open()) //jeśli otwarcie nie powiodło się cout<<"blad otwarcia pliku"; return 1; //kończymy program char c; fin.get(c); while (fin) LUB while (fin.get(c)) cout << c; fin.get(c); fin.close(); return 0; cout<<c;
Odczyt nieformatowany jednoznakowe funkcje wejścia 15 Funkcję get możemy wywoływać kaskadowo, np. char a, b, c; fin.get(a).get(b).get(c); przy czym w ten sposób możemy wczytać dowolne znaki; bez żadnego opuszczania białych znaków, interpretacji znaku końca linii itp. FUNKCJA get DRUGA WERSJA: int get() zwraca wczytany znak w formie liczby typu int; w razie napotkania końca pliku zwrócone zostanie EOF. Ta forma funkcji get nie zwraca referencji do strumienia, więc nie może być używana kaskadowo. int ch; while((ch=cin.get())!= EOF) //przetwarzanie danych
Przykład 2 funkcja get(char &) Napisz program, który oblicza liczbę małych liter, dużych liter, cyfr, spacji i pozostałych znaków znajdujących się w pliku o nazwie podanej przez użytkownika. Jeśli operacje plikowe nie powiodą się program powinien wyświetlić odpowiedni komunikat. #include<iostream> #include<fstream> #include<string> using namespace std; int main() ifstream f; string nazwa; cout<<"podaj nazwę pliku do analizy "; cin>>nazwa; f.open(nazwa.c_str()); //w C++11: f.open(nazwa); if(!f.is_open()) cout<<"blad otwarcia pliku"; return 1; char znak; //liczniki znaków: malych liter, duzych liter, cyfr, spacji, innych int male=0,duze=0,cyfry=0,spacje=0,inne=0; 16
Przykład 2 funkcja get(char &) f.get(znak);//pobieramy znak while(f) //dopóki czytanie się udało if(znak>='a' && znak<='z') male++; else if(znak>='a' && znak<='z') duze++; else if(znak>='0' && znak<='9') cyfry++; else if(znak == ' ') spacje++; else inne++; f.get(znak);//proba pobrania kolejnego znaku f.close(); cout<<"malych liter: " << male << endl; cout<<"duzych liter: " << duze << endl; cout<<"cyfr: " << cyfry << endl; cout<<"spacji: "<< spacje << endl; cout<<"pozostalych znakow: " << inne << endl; return 0; 17
Przykład 2 funkcja get(char &) Napisz funkcję logiczną, który oblicza liczbę małych liter, dużych liter, cyfr, spacji i pozostałych znaków znajdujących się w pliku tekstowym o nazwie podanej jako argument funkcji. Jeśli plik nie został poprawnie otwarty wartością funkcji ma być false, true w przeciwnym przypadku. 1 sposób) bool analiza(string nazwa, int & male, int & duze, int & cyfry, int & spacje, int & inne); 2 sposób) Stwórzmy pomocniczą strukturę: struct Statystyka int male, duze, cyfry, spacje, inne; ; bool analiza2(string nazwa, Statystyka & s) ifstream f; f.open(nazwa.c_str()); //w C++11: f.open(nazwa); if(!f.is_open()) return false; 18
Przykład 2 funkcja get(char &) //zerujemy pola struktury-liczniki znaków: małych liter, // dużych, cyfr,spacji, innych s.male=0; s.duze=0; s.cyfry=0; s.spacje=0; s.inne=0; char znak; while(f.get(znak)) //dopóki czytanie się udało f.close(); if(znak>='a' && znak<='z') s.male++; else if(znak>='a' && znak<='z') s.duze++; else if(znak>='0' && znak<='9') s.cyfry++; else if(znak == ' ') s.spacje++; else s.inne++; return true; 19
Przykład 2 20 int main() Statystyka stat; if ( analiza2("dane.txt", stat) ) cout<<"malych liter: " << stat.male << endl; cout<<"duzych liter: " << stat.duze << endl; cout<<"cyfr: " << stat.cyfry << endl; cout<<"spacji: "<< stat.spacje << endl; cout<<"pozostalych znakow: " << stat.inne << endl; else cout<<"blad operacji plikowych"<<endl; return 0;
put 21 Jednoznakowa funkcja do zapisu: ostream& put(char c) wstawia znak (bajt) c do strumienia. Zwraca referencję do strumienia, na rzecz którego metoda została wywołana. PRZYKŁAD 4. Napisz funkcję o nagłówku: bool kopiasp_(string nwe, string nwy); która tworzy kopię pliku o nazwie nwe do pliku o nazwie nwy, przy czym zamienia znaki spacji na znaki podkreślenia '_'. Funkcja zwraca true jeśli operacje plikowe się powiodły, w przeciwnym razie false. Dla pliku pustego tworzy plik pusty.
Przykład 3 bool kopiasp_(string nwe, string nwy) ifstream we(nwe.c_str()); if(!we.is_open()) return false; ofstream wy(nwy.c_str()); if(!wy.is_open()) we.close(); return false; char zn; while(we.get(zn)) if(zn == ' ') wy.put('_'); // lub wy<<'_'; else wy.put(zn); // lub wy<<zn; we.close(); wy.close(); return true; 22
23 Przykład 3 int main() string nwe,nwy; cout<<"podaj nazwa pliku we "; cin>>nwe; cout<<"podaj nazwa pliky wy "; cin>>nwy; if(kopiasp_(nwe, nwy)) cout<<"ok- kopia"<<endl; else cout<<"blad otwarcia pliku"<<endl; return 0;