-przekazywanie strumieni do funkcji -funkcje … Struktura programu działającego na plikach 5)...

23
Dorota Pylak Pliki – wykład 2 -przekazywanie strumieni do funkcji -funkcje get(char &) i getline(string)

Transcript of -przekazywanie strumieni do funkcji -funkcje … Struktura programu działającego na plikach 5)...

Page 1: -przekazywanie strumieni do funkcji -funkcje … 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

Dorota Pylak

Pliki – wykład 2

-przekazywanie strumieni do funkcji-funkcje get(char &) i getline(string)

Page 2: -przekazywanie strumieni do funkcji -funkcje … 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

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; }

Page 3: -przekazywanie strumieni do funkcji -funkcje … 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

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

Page 4: -przekazywanie strumieni do funkcji -funkcje … 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

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.

Page 5: -przekazywanie strumieni do funkcji -funkcje … 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

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

Page 6: -przekazywanie strumieni do funkcji -funkcje … 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

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;

}

Page 7: -przekazywanie strumieni do funkcji -funkcje … 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

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;}

Page 8: -przekazywanie strumieni do funkcji -funkcje … 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

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;

}

Page 9: -przekazywanie strumieni do funkcji -funkcje … 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

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;}

Page 10: -przekazywanie strumieni do funkcji -funkcje … 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

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;}

Page 11: -przekazywanie strumieni do funkcji -funkcje … 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

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;}

Page 12: -przekazywanie strumieni do funkcji -funkcje … 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

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.

Page 13: -przekazywanie strumieni do funkcji -funkcje … 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

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

Page 14: -przekazywanie strumieni do funkcji -funkcje … 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

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.

Page 15: -przekazywanie strumieni do funkcji -funkcje … 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

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 }

Page 16: -przekazywanie strumieni do funkcji -funkcje … 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

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;}

Page 17: -przekazywanie strumieni do funkcji -funkcje … 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

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.

Page 18: -przekazywanie strumieni do funkcji -funkcje … 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

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;}

Page 19: -przekazywanie strumieni do funkcji -funkcje … 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

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;

}

Page 20: -przekazywanie strumieni do funkcji -funkcje … 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

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'

Page 21: -przekazywanie strumieni do funkcji -funkcje … 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

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.

Page 22: -przekazywanie strumieni do funkcji -funkcje … 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

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

Page 23: -przekazywanie strumieni do funkcji -funkcje … 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

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;}