2. Podstawowe aplikacje i komendy File – Plik - operacje na plikach ...
-przekazywanie strumieni do funkcji -funkcje … Struktura programu działającego na plikach 5)...
Transcript of -przekazywanie strumieni do funkcji -funkcje … Struktura programu działającego na plikach 5)...
Dorota Pylak
Pliki – wykład 2
-przekazywanie strumieni do funkcji-funkcje get(char &) i getline(string)
2
Struktura programu działającego na plikach
1) Dyrektywa preprocesora#include <fstream> //zapewnia dostęp do klas ifstream i ofstream
2) deklaracja zmiennej (strumienia) typu ofstream (zapis do pliku)
ofstream plik;
//lub ifstream (czytanie z pliku)
ifstream plik;
3) wywołanie funkcji wiążącej strumień z konkretnym plikiem i otwierającej go
plik.open("nazwa_pliku.txt");
Kroki 2) i 3) tj. utworzenie obiektu i skojarzenie go z plikiem możemy też zrealizować za pomocą jednej instrukcji:
ofstream plik("nazwa_pliku.txt"); //2)3) drugi sposób
//lub
ifstream plik("nazwa_pliku.txt");
4) sprawdzenie czy otwarcie się powiodło np.
if(!plik.is_open()) //otwarcie nie powiodło się { cout<<"Blad otwarcia pliku"; return 1; }
3
Struktura programu działającego na plikach
5) działania na plikach: możemy teraz używać strumienia plik w taki sam sposób jak obiektu cout w przypadku pliku do zapisu
plik << "jakieś dane do zapisu" << endl << "kolejne dane";
lub jak obiektu cin w przypadku pliku do odczytu
np. dla pliku zawierającego liczby całkowite oddzielone znakami białymi
int x; while (plik>>x) { //jakieś operacje na x }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++ 98od C++11 istnieje druga wersja funkcji open, z parametrem typu string plik.open( sNazwaPliku ); //C++ 11
4
Przykład 2: pliki i funkcje
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.
5
Przykład 2 (funkcja dostaje nazwę pliku)
#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())
return false;
suma=0.0; //zmienna na sumę
double x; //zmienna do której będziemy pobierać kolejne liczby z pliku
6
Przykład 2 (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 } while(pwe>>x) //pobieramy kolejne elementy z pliku
suma+=x;
//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;
}
7
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;}
8
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) //sprawdzamy stan strumienia, jeśli jest w stanie błędu
return false;
suma = 0.0; //zerujemy sumę
double x; //zmienna do której będziemy pobierać elementy z pliku
if (!(pwe>>suma)){
//jeśli czytanie pierwszego elementu się nie udało
return false; //plik pusty lub błąd
} while(pwe >> x) //pobieramy kolejne elementy
suma += x; //jeśli wczytaliśmy element zwiększamy sumę
return true;
}
9
Przykład 2 (funkcja dostaje referencję na strumień)
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;}
10
Przykład 2 wersja z dokładnym określeniem rodzaju błędu
enum ResultType{OK, OPEN_ERROR, EMPTY_FILE, READ_ERROR};
//funkcja oblicza sumę liczb w pliku, dokładnie określany jest rodzaj błędu
ResultType SumaPlikEnum(string nazwa, double &suma)
{ ifstream pwe(nazwa.c_str()); //w C++11: ifstream pwe(nazwa); if(!pwe.is_open()) return OPEN_ERROR; suma=0.0; double x; if (!(pwe>>suma)){ pwe.close(); return pwe.eof() ? EMPTY_FILE : READ_ERROR; //plik pusty lub błąd } while(pwe>>x) suma += x; if(!pwe.eof()){ //jesli nie doszliśmy do końca pliku pwe.close(); return READ_ERROR; } pwe.close(); return OK;}
11
Przykład 2 wersja z dokładnym określeniem rodzaju błędu
int main(){ //funkcja z wykorzystaniem enum string nplik; cout << "podaj nazwe pliku: ";
cin >> nplik; double suma; switch(SumaPlikEnum(nplik,suma)) { case OK: cout<<"suma elementow= "<<suma<<endl; break;
case OPEN_ERROR: cout<<"blad otwarcia pliku "<<endl; break; case EMPTY_FILE: cout<<"plik pusty"<<endl; case READ_ERROR: cout<<"blad odczytu z pliku"<<endl; } return 0;}
12
Operatory << i >>
Operator '<<' lub '>>' jest funkcją, która prócz wykonania samej operacji we/wy, zwraca jako rezultat referencję do tego samego obiektu-strumienia, który był jej pierwszym (lewym) argumentem.
Domyślnie zmienne typów wbudowanych wyprowadzane są następująco:
Strumienie wyjściowe:
wartości całkowite (jak int, short, itd.): w systemie dziesiętnym;
wartości znakowe: jako pojedyncze znaki;
wartości zmiennopozycyjne (czyli float, double i long double): w systemie dziesiętnym, z precyzją 6 cyfr. Precyzja oznacza liczbę cyfr znaczących, a nie liczbę cyfr po kropce dziesiętnej. Końcowe zera po kropce dziesiętnej nie są pisane. Tak więc wartość 1.129996 zostanie najpierw zaokrąglona do sześciu cyfr znaczących, dając 1.13000, a następnie wypisana jako 1.13 (ale na przykład wydruk 1.129994 da 1.12999). Jeśli po kropce wystąpiłyby same zera, to opuszczane są i zera, i sama kropka dziesiętna. Jeśli liczba cyfr przed kropką wynosi lub przekracza sześć, to wypisane zostaną wszystkie, a część ułamkowa zostanie pominięta;
wartości logiczne (typu bool): jako liczby 1 i 0.
Białymi znakami są nazywane te symbole, które są używane w tekście i nie posiadają swojej reprezentacji graficznej, np. spacja, tabulacja, znak przejścia do nowej linii.
Funkcja int isspace( int ch ) zwraca wartość różną od zera, gdy argument ch jest białym znakiem, zero w przeciwnym przypadku.
13
Operatory << i >>
Strumienie wejściowe:
● wiodące białe znaki (spacje, tabulacje, znak nowej linii) są pomijane;
● każda następna niepusta sekwencja białych znaków jest interpretowana jako koniec danych. Znaki te pozostają w buforze i będą pominięte, jako wiodące, przy następnym czytaniu;
● liczby całkowite wczytywane są w postaci dziesiętnej; pierwszym niebiałym znakiem może być znak '+' lub '-', następnie wczytywane są cyfry aż do napotkania znaku nie będącego cyfrą — ten znak jest pozostawiany w buforze i będzie pierwszym znakiem wczytanym przez następną operację czytania. Na przykład, jeśli program czyta za pomocą instrukcji 'cin >> x >> y', to można podać jako dane 128-25; wczytywanie 128 do x zakończy się na znaku minus, który pozostanie w strumieniu. Następnie będzie kontynuowane czytanie do y znaków -25;
● liczby zmiennopozycyjne wczytywane są w formacie liczb całkowitych bez kropki dziesiętnej, w formacie z kropką dziesiętną i w notacji „naukowej” (na przykład 1e-1 to to samo co 0.1, 1.201e+2 to 120.1);
● wartości logiczne mają postać literałów 1 i 0.
Operator >> zawsze pomija wszelkie napotkane białe znaki
14
Odczyt/zapis nieformatowany
● 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.
● Istnieją też operacje we/wy nieformatowane, które czytają lub piszą „surowe” bajty —
bez ich żadnej interpretacji.
● 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, więc może być używana kaskadowo.
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.
15
Odczyt nieformatowany – jednoznakowe funkcje wejścia
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ą
ifstream fin("dane.txt");
char c;
while (fin.get(c)) cout << c;
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.
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 }
16
Przykład 3 – funkcja get(char &)
Napisz funkcję, która dla pliku o nazwie podanej przez pierwszy parametr funkcji oblicza prawdopodobieństwo z jakim znak podany przez drugi parametr występuje w pliku (tj. liczbę wystąpień znaku / liczbę wszystkich znaków). Jeśli operacje plikowe nie powiodą się lub plik jest pusty, wartością funkcji ma być –1.
float prawdopZnaku(string n, char szuk){ ifstream we; we.open(n.c_str()); //c++98 lub we.open(n); //c++11 if(!we.is_open()) return -1; char zn; int ilew=0, ileszuk=0; while(we.get(zn)){ ilew++; if(zn == szuk) ileszuk++; } we.close(); if(ilew) return ileszuk*1.0f/ilew; else //plik pusty return -1;}
17
Przykład 3
int main()
{
float p = prawdopZnaku("t.txt",'\n');
if(p<0)
cout<<"blad operacji plikowych"<<endl;
else
cout<<"prawdopodobienstwo = "<< p <<endl;
return 0;
}
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.
18
Przykład 4
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;}
19
Przykład 4
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"<<endl;
else
cout<<"blad otwarcia pliku"<<endl;
return 0;
}
21
getline(string)
Wprowadzanie danych do obiektów string:
string s;
cin >> s; //wczytuje słowo
getline(cin, s); //wczytuje linię, pomija \n
Mamy dwie wersje funkcji getline():
istream& getline (istream& is, string& str);
istream& getline (istream& is, string& str, char delim);
druga pozwala na określenie znaku kończącego wczytywanie. Powyższe wersje funkcji dla klasy string automatycznie dopasowują rozmiar obiektu string do liczby zapisywanych znaków
string lname;
cin>>lname; //potrafi wczytać bardzo długie słowo
getline(cin, lname); //wczytuje linię, obcina '\n'
22
getline(string)
Istnieją pewne ograniczenia na długość wprowadzanego ciągu do obiektu string:
1) maksymalny dopuszczalny rozmiar przechowywany w stałej string::npos (zazwyczaj jest to maksymalna wartość typu unsigned int)
2) ilość pamięci dostępnej w programie
Funkcja getline() klasy string wczytuje wprowadzane znaki i zapisuj je w obiekcie klasy string, dopóki nie zajdzie jedna z trzech okoliczności:
a) program natrafi na koniec pliku
b) program natrafi na znak kończący wczytywanie, domyślnie jest to \n. Znak ten jest pobierany ze strumienia, ale nie jest zapisywany do obiektu string
c) zostanie wczytana maksymalna liczba znaków, czyli mniejsza z wartości: string::npos oraz liczby bajtów pamięci dostępnych na zapisanie ciągu.
Funkcja operator>>() w klasie string czyta słowo, czyli omija wiodące znaki białe, a potem dochodzi do znaku białego i zostawia go w kolejce wejścia.
Powyższych funkcji klasy string możemy używać do wczytywania plików.
23
Przykład 5 - getline(string)
Napisz funkcję, która policzy liczbę wszystkich linii oraz liczbę pustych linii w pliku tekstowym, którego nazwa jest parametrem funkcji. Wynikiem funkcji jest liczba linii lub -1, jeśli otwarcie pliku nie powiodło się. Liczbę pustych linii przekazujemy dodatkowym parametrem. Jeśli linia zawiera jakieś znaki białe, to nie jest pustą linią.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int ileLinii(string nplik, int &ilpuste)
{
ifstream pwe(nplik.c_str());
if(!pwe.is_open()) return -1;
//plik istnieje i jest otwarty
string linia;
int ile=0; //liczba wszystkich linii w pliku
ilpuste=0; //liczba pustych linii - zerujemy parametr
24
Przykład 5 - getline(string)
while(getline(pwe,linia)) { //wczytano linie ile++; //sprawdzamy czy pusta if (linia.empty()) //lub if(linia.size()==0) ilpuste++; } pwe.close(); return ile;}int main(){ string nazwaplik; cout << "Podaj nazwe pliku: "; cin>>nazwaplik; int ilp; int ill= ileLinii(nazwaplik, ilp); if(ill<0) cout<<"Blad otwarcia pliku"<<endl; else{ cout<<"W pliku "<<nazwaplik<<" mamy "<<ill<<" wszystkich linii " <<endl<<"w tym pustych: "<<ilp<<endl; } return 0;}