Pliki i manipulatory - Łańcuchowe funkcje wejścia - getline(string) Dorota Pylak - Tryby otwarcia pliku - Formatowanie manipulatory
2 Łańcuchowe funkcje wejścia - 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'
getline(string) 3 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.
4 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
Przykład 5 - getline(string) 5 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;
6 Tryby otwarcia pliku * Tryb otwarcia pliku określa sposób w jaki dany plik ma być używany: do odczytu, do zapisu, w celu dołączenia itd. Przy kojarzeniu strumienia z plikiem (konstruktor lub open()) możemy podać drugi argument określający tryb otwarcia pliku: //konstruktor z argumentem określającym typ ifstream fin("dane_we.txt", mode1); //metoda open() z argumentem określającym typ ofstream fout; fout.open("wyniki.txt", mode2); W klasie ios_base są zdefiniowane stałe trybu otwarcia (typu bitmask) Stałe ios_base::app Znaczenie (append) Dołącz na koniec pliku ios_base::ate (at end) Po otwarciu pliku ustaw się na jego końcu ios_base::binary (binary) Plik binarny ios_base::in (input) Otwórz plik do odczytu ios_base::out (output) Otwórz plik do zapisu ios_base::trunc (truncate) Zredukuj rozmiar pliku do zera jeśli istnieje
7 Tryby otwarcia pliku * W omawianych do tej pory przykładach używaliśmy tylko jednego argumentu: ofstream we; we.open("plik.txt"); W prototypach tych funkcji określono dla drugiego argumentu - trybu otwarcia wartości domyślne. W konstruktorze i metodzie open() klasy ifstream domyślną wartością drugiego argumentu jest stała ios_base::in (otwórz do odczytu), a w przypadku klasy ofstream domyślną wartością jest wyrażenie ios_base::out ios_base::trunc (otwórz do zapisu i zredukuj rozmiar pliku do zera). Operator alternatywy bitowej ( ) łączy dwie wartości bitowe w pojedynczą wartość, przy użyciu której można ustalić obydwa bity. W przypadku klasy fstream nie określono trybu domyślnego, a więc przy tworzeniu obiektu tej klasy trzeba podać go jawnie. Jeśli chcemy zachować zawartość pliku i dołączyć nowe dane na jego końcu, możemy użyć trybu ios_base::app: ofstream fout("dolacz.txt", ios_base::out ios_base::app);
Dołączanie pliku #include <iostream> #include <fstream> #include <string> using namespace std; int main() { // dodawanie danych nowych osób do pliku ofstream fout("plik.txt", ios_base::out ios_base::app); if (!fout.is_open()) { cerr << "Nie mozna otworzyc pliku do zapisu.\n"; return 1; cout << "Podaj dane kolejnych osób (aby zakonczyc, wprowadz pusty wiersz):\n"; string dane; while (getline(cin, dane) && dane.size() > 0) { fout << dane << endl; fout.close(); return 0; 8
Stany strumienia * 9 Przyjrzyjmy teraz co się dzieje w przypadku nieprawidłowych danych wejściowych. Strumienie zawierają składową typu iostate (dziedziczona po klasie ios_base) opisująca stan strumienia. Na stan strumienia składają się trzy elementy klasy ios_base: eofbit, badbit oraz failbit. Każdy z elementów jest pojedynczym bitem i może przyjmować wartość 1(ustawiony) lub 0(skasowany). W przypadku, gdy operacja pobierania danych ze strumienia napotka koniec pliku ustawia bit eofbit. Gdy odczyt oczekiwanego znaku kończy się niepowodzeniem zostaje ustawiony bit failbit. Ustawienie bitu failbit mogą również spowodować nieudane operacje wejścia-wyjścia, takie jak próba odczytu niedostępnego pliku czy też próba zapisu do pliku chronionego przed zapisem. Element badbit jest ustawiany w sytuacji, gdy strumień został uszkodzony na skutek jakiegoś nierozpoznanego błędu. Różne implementacje różnią się od siebie tym, które zdarzenia powodują ustawienia bitu failbit, a które badbit. Jeśli wszystkie trzy bity są wyzerowane oznacza to, że wszystko jest w porządku. Stan strumienia można badać za pomocą funkcji składowych (metod) klasy ios: bool bad( ); bool eof( ); bool fail( ); bool good( ); zwracających w postaci wartości logicznej ustawienie odpowiednich bitów.
Formatowanie manipulatory i flagi 10 Sposób formatowania danych w operacjach wyjścia można zmienić. Służą do tego flagi (znaczniki) formatowania (format flags) i manipulatory. Manipulatory są to funkcje zdefiniowane w klasie ios i wywoływane poprzez podanie ich nazw jako elementów wstawianych do lub wyjmowanych ze strumienia. Ich działanie może, ale nie musi, polegać na modyfikacji flagi stanu formatowania strumienia. Do manipulowania flagą stanu formatowania danego strumienia służą flagi zdefiniowane w klasie bazowej ios_base a tym samym w klasie pochodnej ios jako składowe statyczne. Odwołujemy się do zatem do nich poprzez operator zakresu klasy, np. ios::left, ios::scientific itd. Flagi te są, jak i cała flaga stanu formatowania, typu ios_base::fmtflags. Funkcja ios::fmtflags setf(ios::fmtflags flg) zmienia flagę stanu formatowania ORując ją z flagą flg; zwraca flagę sprzed zmiany. Funkcja ios::fmtflags setf(ios::fmtflags flg, ios::fmtflags pole) zmienia flagę stanu formatowania ORując ją z flagą flg pochodzącą z pola pole; usuwa ustawienie innych flag pochodzących z tego samego pola; zwraca flagę stanu sprzed zmiany.
Formatowanie manipulatory bezargumentowe Manipulatory bezargumentowe Manipulatory bezargumentowe wstawia się do strumienia nie podając nawiasów. Mają one nazwy takie jak flagi. dec, hex, oct ustawiają podstawę wyprowadzanych liczb całkowitych na: dziesiętną (domyślnie), szesnastkową lub ósemkową. Zmiana jest trwała: aby przywrócić poprzednie ustawienia, trzeba do strumienia wstawić odpowiedni manipulator lub na rzecz strumienia wywołać jedną z metod zmieniających flagę stanu formatowania showbase, noshowbase przy wypisywaniu zawsze pokaż podstawę (wiodące 0 lub 0x w systemie ósemkowym i szesnastkowym) albo nie pokazuj podstawy. int n=15; cout<<"n = "<<n<<endl; //podstawa domyślna cout<<"n (oct): "<<oct<<n<<endl; //zamiana podstawy na 8-kowa cout<<"n (oct z showbase): " <<showbase<<n<<endl; cout<<"n (hex z showbase): "<<hex<<n<<endl; //zamiana podstawy na 16-kowa cout<<"n (hex z noshowbase): "<<noshowbase<<n<<endl; cout<<dec<<"n = "<<n<<endl; //przywrócenie podstawy 10 Output: n = 15 n (oct ): 17 n (oct z showbase): 017 n (hex z showbase): 0xf n (hex z noshowbase): f n = 15 11
Formatowanie manipulatory bezargumentowe 12 int i; cout<< "podaj liczbe w systemie szesnastkowym "; cin>> hex >> i; //pobieranie wartosci 16-tkowej cout<< i << endl; //wyświetlenie w systemie 10-tkowym Output: podaj liczbe w systemie szesnastkowym A 10 fixed, scientific ustawiają formatowanie wyprowadzanych liczb zmiennopozycyjnych. W notacji naukowej (scientific) wypisywana jest jedna cyfra przed kropką, maksymalnie tyle cyfr po kropce, ile wynosi aktualna precyzja, następnie litera 'e' i wykładnik potęgi dziesięciu, przez którą należy pomnożyć liczbę znajdującą się przed literą 'e'. Jeśli liczba jest ujemna, to przed nią wypisywany jest znak minus. Na przykład 1.123456e2 oznacza 112.3456, natomiast -1.123456e-3 oznacza -0.001123456. Format normalny (fixed) oznacza wypisywanie maksymalnie tylu cyfr po kropce dziesiętnej, ile wynosi aktualna precyzja. Jeśli żaden format nie jest ustawiony, to użyty będzie format ogólny, który pozostawia implementacji sposób zapisu (naukowy lub normalny) w zależności od wartości liczby tak, żeby zapis z ustaloną precyzją zajmował jak najmniej miejsca
13 Formatowanie - manipulatory double x = 123.4567891; double y = 12345678.91; //format ogólny cout<< "1) x = " << x << endl; cout<< "2) y = " << y << endl; cout<< fixed << "3) y = " << y << endl; //format z. cout<< scientific << "4) x = " << x << endl; //format naukowy Output: 1) x = 123.457 2) y = 1.23457e+07 3) y = 12345678.910000 4) x = 1.234568e+02 showpos, noshowpos- znak plus przed liczbami dodatnimi (domyślnie dla wartości dodatnich znak plus jest pomijany) lub liczby dodatnie nie są poprzedzane znakiem plus int a = 123; cout<< showpos <<"a = "<< a <<endl; //a = +123
Formatowanie - manipulatory 14 showpoint, noshowpoint zawsze wypisz kropkę dziesiętną i końcowe zera w części ułamkowej (domyślnie: NIE) albo wynik bez kropki jeśli część ułamkowa jest zerowa double b = 1234; cout<< b << endl; //1234 cout<< showpoint << b << endl; //1234.00 uppercase litery występujące w zapisach liczb mają być duże (E, X) double z = 1234.5678; cout<<uppercase<<scientific<<z<<endl; //1.234568E+03 cout<<hex<<showbase<<16<<endl; //0X10 nouppercase litery występujące w zapisach liczb mają być małe (e, x) left, right ustawiają sposób wyrównywania (justowania) wyprowadzanych danych: do lewej, prawej. flush powoduje opróżnienie strumienia wyjściowego endl powoduje wysłanie do strumienia wyjściowego znaku końca linii i opróżnienie bufora związanego z tym strumieniem, czyli natychmiastowe wyprowadzenie znaków z bufora do miejsca przeznaczenia (na ekran, do pliku itd). ends powoduje wysłanie do strumienia wyjściowego znaku końca napisu, czyli ' \0'. boolalpha, noboolalpha wstawianie do strumienia wynikowego wartości logicznych w postaci słów false i true albo liczb 0 lub 1
15 Manipulatory z argumentami Istnieją też manipulatory argumentowe. Stosuje się je analogicznie jak manipulatory bezargumentowe, ale wymagają one argumentów, które, jak dla zwykłych funkcji, podaje się w nawiasach. Aby używać predefiniowanych manipulatorów argumentowych, należy dołączyć plik nagłówkowy #include<iomanip>. Predefiniowane manipulatory argumentowe zwracają, jak wszystkie manipulatory, referencję do strumienia do którego zostały wstawione setw(int szer) ustawia szerokość pola dla najbliższej operacji na strumieniu, przy czym określana jest minimalna szerokość pola: jeśli dana zajmuje więcej znaków, to odpowiednie pole zostanie zwiększone. Domyślną wartością szerokości pola jest zero, czyli każda wypisywana dana zajmie tyle znaków, ile jest potrzebne, ale nie więcej int e = 1, f = 10; cout<< "--------" << setw(4) << e << setw(4) << f <<endl; cout<< left << setw(4) << e << setw(4) << f << endl <<"--------"; -------- 1 10 1 10 --------
16 Manipulatory z argumentami setfill(char_type znak) ustawia znak, którym będą wypełniane puste miejsca jeśli szerokość pola wydruku jest większa niż liczba wyprowadzanych znaków (domyślnie jest to znak spacji). int j = 1, k = 10; cout << right << setw(6) << setfill('0') << j << endl; //000001 cout << left << setw(6) << setfill('0') << j << endl; //100000 cout << right; //powrót do wyrównania do prawej cout << setw(5) << setfill('*'); cout <<'a'<< endl; //****a //dopoki nie zmienimy wypełnienia, mamy wypelnienie '*' cout << setw(4) << j << setw(4) << k << endl; //***1**10 cout << left << setw(4) << j << setw(4) << k << endl; //1***10** //powrot do standardowych ustawien cout << right << setfill(' '); cout << setw(4) << j << setw(4) << k << endl; // 1 10
17 Manipulatory z argumentami setprecision(int prec) ustawia precyzję wyprowadzanych liczb zmiennopozycyjnych. W przypadku formatu fixed jest to ilość cyfr po kropce dziesiętnej, w przypadku formatu naukowego scientific jest to ilość cyfr mantysy po kropce, a dla formatu ogólnego jest to całkowita liczba cyfr liczby, chyba, że liczba ma mniej cyfr. Domyślnie precyzja ustawiona jest na 6. // setprecision example #include <iostream> #include <iomanip> using namespace std; int main () { // std::cout, std::fixed // std::setprecision double d = 3.14159, int g=1234567.89; cout<<g<<endl; //1.23457e+06 cout << setprecision(5) << d << '\n'; //3.1416 cout << setprecision(9) << d << '\n'; //3.14159 cout << fixed; cout << setprecision(5) << d << '\n'; //3.14159 cout << setprecision(9) << d << '\n'; //3.141590000 return 0;
18 Przykład 1 - zapis struktury do pliku z formatowaniem Dane są typy strukturalne: struct TPrzesylka{ ; string skad, dokad; float waga; //w kg bool priorytet; //wartosc prawda dla przesyłki priorytetowej struct TListaPrzesylek{ ; int ile_p; //liczba przesyłek, ile_p <= 100 TPrzesylka tab_p[100]; //tablica zawiera dane o wszystkich przesylkach Napisz funkcje o nagłówku int zapiszprzesylki(const TListaPrzesylek& listap, string nazwapl); która zapisze do pliku o nazwie przekazanej w parametrze nazwapl pełną informację o przesyłkach priorytetowych ze struktury listap. Wartością funkcji jest ilość zapisanych przesyłek. Jeśli w strukturze brak jest przesyłek priorytetowych, to w pliku zapisujemy komunikat "Brak przesylek priorytetowych". Jeśli nie uda się otwarcie pliku o zadanej nazwie, wartością funkcji ma być 1. Zadbaj o formatowanie danych w pliku: numer przesyłki z wyrównaniem do prawej, pola string wyjustowane do lewej, waga z dokładnością do 2-ch miejsc po przecinku z wyrównaniem do prawej.
Przykład 1 zapis struktury do pliku z formatowaniem 19 #include <iostream> #include <fstream> #include <string> #include <iomanip> using namespace std; struct TPrzesylka{ string skad, dokad; float waga; bool priorytet; //wartosc prawda dla przesyłki priorytetowej ; struct TListaPrzesylek{ int ile_p; //liczba przesyłek, ile_p <= 100 TPrzesylka tab_p[100]; //tablica zawiera dane o wszystkich przesylkach ;
Przykład 1 zapis struktury do pliku z formatowaniem 20 int zapiszprzesylki(const TListaPrzesylek& lp, string nazwapl){ ofstream pwy(nazwapl.c_str()); if (!pwy.is_open()) //jesli otwarcie pliku nie powiodlo sie return -1; int ile=0; //licznik przesylek priorytetowych pwy<<fixed<<setprecision(2); //ustawiamy precyzje na 2 miejsca po, for (int i=0; i < lp.ile_p; i++) //przechodzimy po liscie przesylek { if(lp.tab_p[i].priorytet) //jesli jest priorytetowa { ile++;//zwiekszamy licznik i zapisujemy do pliku pwy<<setw(3)<<ile<<". "<<left<<setw(20)<<lp.tab_p[i].skad <<" - "<<setw(20)<<lp.tab_p[i].dokad<<" - " <<right<<setw(6)<<lp.tab_p[i].waga<<" kg"<<endl; if(ile==0) pwy<<"brak przesylek proiorytetowych"<<endl; pwy.close(); return ile;
Przykład 1 zapis struktury do pliku z formatowaniem 21 int main() { TListaPrzesylek lista={4,{{"warszawa", "Koluszki", 2.55, true, {"Warszawa", "Gniezno", 8, false, {"Krakow", "Szczecin",45.2, true, {"Katowice", "Poznan", 123.4, true; int ilep = zapiszprzesylki(lista,"priorytetowe.txt"); if(ilep<0) cout<<"blad otwarcia pliku"<<endl; else cout<<"przesylki zapisano do pliku"<<endl; return 0; Zawartość pliku: 1. Warszawa - Koluszki - 2.55 kg 2. Krakow - Szczecin - 45.20 kg 3. Katowice - Poznan - 123.40 kg
22 Przykład 2- zapis macierzy do pliku z formatowaniem Napisz i przetestuj funkcje logiczną o nagłówku bool zapiszmacierz(string nazwapl, float mac[][nmax], int w, int k ); która do pliku o nazwie przekazanej przez parametr nazwapl zapisze macierz mac o w- wierszach i k- kolumnach, gdzie w,k <= NMAX. Jeśli operacje plikowe powiodą się zwraca true, a false w przeciwnym przypadku. Zadbaj odpowiednie formatowanie wyników. #include <iostream> #include <fstream> #include <string> #include <iomanip> using namespace std; const int NMAX = 10; //teraz przed funkcja main() tylko deklaracja funkcji, definicja po main() //w prototypie- deklaracji funkcji można opuścić nazwy parametrów bool zapiszmacierz(string, float [][NMAX], int, int );
Przykład 2- zapis macierzy do pliku z formatowaniem 23 int main() { float t[][nmax] = { { 1, 3, 5, 23, 16.42, {4.567, 4, 6, 234.345, 98, { 585, 34, 1, 67, 31.2, { 1, 0, 1, 2345.967, 123.2; string n; cout<<"podaj nazwe pliku do zapisu "; cin>>n; if( zapiszmacierz(n,t,4,5)) cout<<"macierz zapisana do pliku"<<endl; else cout<<"blad otwarca pliku"<<endl; return 0;
Przykład 2- zapis macierzy do pliku z formatowaniem 24 //definicja funkcji tu koniecznie nazwy parametrów bool zapiszmacierz(string nazwapl, float mac[][nmax], int w, int k ) { ofstream pwy(nazwapl.c_str()); if (!pwy.is_open()) //jesli otwarcie pliku nie powiodlo sie return false; pwy<<fixed<<setprecision(3); for(int i=0; i<w; i++)//idziemy po wierszach od 0 do w-1 { for (int j=0; j<k; j++)//wzdluz i-tego wiersza(po kolumnach) { pwy<<setw(9)<<mac[i][j]; //po wyswietleniu calego wiersza wstawiamy znak nowej linii pwy<<endl; pwy.close(); return true;
Manipulatory * 25 setbase(int baza) zmienia podstawę używaną przy wyprowadzaniu liczb całkowitych. skipws, noskipws pomijanie albo niepomijanie białych znaków przy wprowadzaniu ze strumieni wejściowych. Standardowo jest ustawiony manipulator skipws. Natomiast użycie manipulatora noskipws spowoduje, że białe znaki nie będą pomijane przy czytaniu. Uwaga: pewne operacje ekstrakcji ze strumienia używają białych znaków jako separatora, w takim przypadku jeśli noskipws jest ustawione, żaden znak nie może być pobrany. #include <iostream> // std::cout, std::skipws, std::noskipws #include <sstream> // std::istringstream bo rolę plików mogą też pełnić napisy using namespace std; int main () { char a, b, c; //otwieramy strumień którego źródłem jest obiekt klasy string istringstream iss (" 123");//strumień korzystający z napisów iss >>skipws >> a >> b >> c;//pomijamy białe znaki cout << a << b << c << '\n'; //123 iss.seekg(0); //powrot na poczatek iss >> noskipws >> a >> b >> c; //nie pomijamy białych znaków cout << a << b << c << '\n'; // 1 return 0;
Manipulatory * 26 setiosflags(ios::fmtflags flag) modyfikuje flagę formatowania tak jak jednoargumentowa metoda setf, czyli ORuje flag z flagą stanu formatowania. resetiosflags(ios::fmtflags flag) odpowiednik metody unsetf.