Archiwa tagu: praca z plikiem

C++ kurs podstawy programowania. Praca z plikami tekstowymi, odczyt.

Odczyt z pliku.
Umiemy już zapisać dane do pliku tekstowego więc przyszedł czas na odczyt danych tych danych. Skoro przed chwilą napisaliśmy program do zapisu danych to pewnie wystarczy trochę go tylko zmodyfikować/przerobić na odczyt danych. Odpowiedź brzmi tak i właśnie taką modyfikację zaraz zrobimy. Przypomnienie listingu dotyczącego zapisu danych do pliku.

Listing

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::out | ios::app);

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;
    cout << "Slowka zapisane" << endl;

    moj_plik.close();

    return 0;
}

Zaczniemy od zmodyfikowania linii która mówi w jakim trybie otwieramy nasz plik, czy do zapisu czy do odczytu? Skoro atrybut ios::out funkcji open() mówił o tym, że otwieramy plik do zapisu to wystarczy go zmodyfikować na postać ios::in. Atrybut ios::app w ogóle usuwamy bo do naszego zadania nie jest on w ogóle potrzebny. Czyli linia dotycząca dostępu do pliku będzie wyglądała tak.

moj_plik.open( "zwierzeta.txt" , ios::in );

Dobrą praktyką jest sprawdzenie, czy z plikiem jest wszystko w porządku czy nie jest np. uszkodzony? Jeżeli będziemy chcieli odczytać jakieś dane z uszkodzonego pliku, nasz program zakończy się błędem. Do tego celu służy funkcja good(). A sprawdzenie będzie wyglądało tak.

Listing

    if(moj_plik.good()==false)
    {
        cout << "Plik jest uszkodzony!" << endl; // funkcja good() zwrocila falsz
    }
    else
    {
        cout << "Plik jest poprawny!" << endl; // funkcja good() zwrocila prawde
    } 

Funkcja good() zwróci nam wartość fałsz jeżeli plik będzie uszkodzony lub wartość prawda jeżeli z plikiem będzie wszystko OK. Jakby się tak zastanowić to w przypadku poprawnego pliku nie ma potrzeby informowania o tym fakcie użytkownika. Zamiast komunikatu o poprawnym pliku w części else{} będziemy umieszczać kod, który powie komputerowi co ma zrobić w przypadku kiedy z plikiem jest wszystko w porządku.
Skoro z plik jest OK to teraz zamiast zapisywać dane do pliku musimy je odczytać. Do tego celu służy funkcja getline(). Dla tej funkcji podajemy dwa parametry. Pierwszy to do którego pliku chcemy się odwołać (zmienna plikowa), a w drugim (po przecinku) określamy gdzie chcemy zapisać odczytane dane z pliku. Żeby mieć gdzie zapisywać dane z pliku musimy utworzyć zmienną do której będziemy je zapisywali. Stworzymy więc sobie wcześniej zmienną string linia; o typu string (pomiędzy using namespace … funkcją main() ).

string linia;

a następnie w części else{} naszego if-a wrzucimy kod z funkcją getline().

getline(moj_plik, linia);

W tym momencie nasz kod wygląda tak.

Listing

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;
string linia;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::in);

    if(moj_plik.good()==false)
    {
        // funkcja good() zwrocila falsz, blad odczytu
        cout << "Blad odczytu pliku!";
    }
    else
    {
        // funkcja good() zwrocila prawde, czyli z plikiem wszysto OK
        getline(moj_plik,linia);
        cout << linia << endl;
    }

    moj_plik.close();

    return 0;
}

Jak uruchomimy nasz program to zobaczymy na ekranie słówko cat.


cpp c++ kurs podstawy programowania odczyt z pliku tekstowego

Dlaczego tylko słówko cat ? Otóż dlatego, że funkcja getline() pobrała (odczytała) pierwszą linię z naszego pliku i tyle, na razie niczego więcej nie oprogramowaliśmy. Więc, co zrobić żeby można było „przemieszczać” się pomiędzy poszczególnymi liniami?
Każdorazowe wywołanie funkcji getline() skutkuje odczytem aktualnej linii (zaczynając od początku pliku) i przejściem do kolejnej, czyli przy drugim wywołaniu tej funkcji odczytamy drugą linię z pliku itd. W zależności jakie dane odczytujesz, co chcesz z nimi później zrobić i czy wiesz ile plik ma linii, dobierasz metodę do odczytu pliku, ale cały czas korzystasz z funkcji getline(). Weźmy na warsztat nasz plik. Wiemy, że nasz plik przechowuje w kolejnych liniach (na przemian) słówko angielskie i jego polskie tłumaczenie. Aby odczytać dane z pliku moglibyśmy wrzucić poszczególne słówka do tablicy. Wtedy mamy sytuację, że pod indeksami parzystymi (pamiętaj numeracja tablic zaczyna się od 0 a nie od 1) będziemy mieli słówka angielskie a pod indeksami nieparzystymi będziemy mieli słówka polskie. Zawsze polskie tłumaczenie danego angielskiego słówka będzie o indeksie o jeden większym niż słówko angielskie. Oczywiście pomysłów na odczytanie jest pewnie więcej np. wrzucić słówka angielskie do jednej tablicy a polskie od drugiej, wtedy komórki o takim samym indeksie o obu tablicach będą ze sobą związane (słówko angielskie i polskie tłumaczenie). Można też wrzucić słówka do tablicy wielowymiarowej itd. Teraz zajmiemy się tym rozwiązaniem o którym mówiłem na początku, wrzucimy słówka ciągiem do jednej tablicy a później je wypiszemy na ekranie ale już będziemy je pobierać z tablicy a nie z pliku. W tablicach jest tylko jedno ograniczenie, od razu musimy podać długość tej tablicy. Oczywiście możemy to obejść obliczając przed deklaracją tablicy ilość linii w pliku ale nie jest to tematem tej lekcji więc od razu przy deklaracji tablicy wpiszemy odpowiednią jej długość. No to do dzieła.
Do naszego pliku zwierzęta.txt dopisałem jeszcze trzy pozycje, więc w tym momencie mamy już 5 zwierząt: kot, piec, małpa, mysz i koń.
Na początku deklarujemy tablicę, do której wrzucimy słówka z pliku. Zwierząt jest 5 więc słówek jest 10 i tylu elementowej tablicy będziemy potrzebować. Oczywiście tablica będzie przechowywała stringi więc musi być typu string.

string words_all[10];

Teraz standardowa procedura: deklaracja zmiennej plikowej i otwarcie pliku do odczytu.

Listing

// tworzymy zmienną plikową
    fstream file;

    // otwieramy plik do odczytu
    file.open("zwierzeta.txt",ios::in);

    // good() <- sprawdza czy plik OK
    if(file.good()==false)
    {
        // plik NIE jest OK
    }
    else
    {
        // jeżeli plik nie jest uszkodzony to tutaj piszemy dalej kod
    }

Jeżeli w ten sposób „sprawdzamy” czy z plikiem wszystko OK to dalej kod piszemy wewnątrz części else{}. Można by to było oprogramować trochę inaczej.

if(file.good()==false)
    {
        // plik NIE jest OK
       exit(0);  // ale wtedy na górze musisz dodać bibliotekę #include <cstdlib>
    }
    // wtedy nie potrzebujesz części else{}
    // i kod piszesz normalnie poza if-em

Jaką robotę robi tutaj funkcja exit()? Funkcja ta służy do zamknięcia programu (wyjścia z programu). Zwróć uwagę, że uruchamiamy ją (czyli wychodzimy z programu) w momencie kiedy coś jest nie tak z plikiem. Jeżeli np. plik jest uszkodzony nie ma sensu żeby komputer dalej przetwarzał kod i najlepiej wtedy zakończyć program. Wybór metody zależy od Ciebie.
Kod który odczytuje dane z pliku i „wrzuca” je do tablicy poniżej.

// i <- licznik do for'a
        int i=0;
        for( i = 0; i <= 9; i++ )
        {
            getline(file,line);
            words_all[i] = line;
        }

Pętlę for już znamy więc wiemy jak działa. Dlaczego w drugim parametrze w pętli for jest i<=9 ? Skoro numeracja indeksów w tablicy zaczyna się od 0 to od takiej wartości licznika i musimy rozpocząć. Wtedy drugim warunkiem musi być i<=9 bo chcemy wrzucić 10 elementów do tablicy a zaczęliśmy liczyć od 0 więc musimy skończyć na wartości 9.
Znamy już funkcję getline(); i wiemy, że pobiera ona „aktualną” linię do zmiennej line.
Teraz zaczyna się clou sprawy, czyli wrzucanie tych danych do tablicy. Spójrz na zapis.

words_all[i] = line;

Wartość zmiennej line (w danym cyklu pętli for) będziemy wrzucali do kolejnych komórek tablicy words_all[]. Licznik i posłuży nam do odwoływania się do kolejnych elementów (komórek) tablicy. Przy pierwszym cyklu pętli for zmienna i będzie miała wartość 0, czyli powyższe polecenie przyjmie postać words_all[0] =line; co oznacza, że wartość (linia) odczytaną z pliku wrzucimy do komórki o zerowym indeksie w tablicy words_all[]. Przy drugim cyklu, dzięki funkcji getline() w zmiennej line będziemy już mieli odczytaną drugą linię z pliku. Wtedy licznik i dla pętli for przybierze już wartość 1, czyli drugą linię z pliku zapiszemy do komórki o indeksie 1 w tablicy words_all[] itd. Sam widzisz, że to nie jest takie proste. Pętla wykona się 10 razy i wszystkie linie (słówka) z pliku znajdą się w naszej tablicy.
Teraz wystarczy tylko wyświetlić po kolei słówka na ekranie ale już nie z pliku tylko z tablicy, gdzie przed chwilką „wrzuciliśmy” dane z pliku.

Listing

// Odczyt slowek z tablicy
        cout << "Slowka z tablicy words_all:" << endl;
        for( i = 0; i <= 9; i++ )
        {
            cout << words_all[i] << endl;
        }

Sam widzisz, że odczyt z tablicy robimy dokładnie według tego samego schematu gdy zapisywaliśmy do niej dane z tą różnicą że tutaj wywołujemy funkcję cout i odwołujemy się (za każdym „obrotem” pętli) do kolejnych komórek tablicy words_all[].
Cały kod poniżej.

Listing

#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;

string slowkoen, slowkopl;
string linia;

int main()
{
    // deklarujemy tablice na slowka z pliku
    string words_all[10]; 
   // zm. do zapisywania linii odczytanych w pliku
    string line;

    // tworzymy zmienna plikowa
    fstream file;
    
    // otwieramy plik do odczytu
    file.open("zwierzeta.txt",ios::in);

    // good() <- sprawdza czy plik OK
    if(file.good()==false)
    {
        cout << "Blad odczytu pliku!";
        exit(0);
    }
    else
    {
        // i <- licznik do for'a
        int i=0;
        for( i = 0; i <= 9; i++ )
        {
            getline(file,line);
            words_all[i] = line;
        }

        // Odczyt slowek z tablicy
        cout << "Slowka z tablicy words_all:" << endl;
        for( i = 0; i <= 9; i++ )
        {
            cout << words_all[i] << endl;
        }
    }
    file.close();
    return 0;
}

c++ kurs programowania obiektowego spis treści 

 

 

C++ kurs podstawy programowania. Praca z plikami tekstowymi, zapis.

Dzisiaj poznamy bardzo przydatną rzecz a mianowicie nauczymy się odczytywać dane (tekst) z plików. To bardzo przydatna umiejętność. Pliki tekstowe mogą nawet służyć jako takie mini bazy danych więc sam widzisz, że umiejętność pracy z plikami tekstowymi jest naprawdę ważna i przydatna. Zaczynamy!

Startujemy oczywiście z poziomu pustego projektu Hello world! (listing poniżej).

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello world!" << endl;
    return 0;
}

Stworzymy plik, który kiedyś wykorzystamy do stworzenia programu do nauki języka angielskiego. Będzie on przechowywał w kolejnych liniach najpierw słówko angielskie a w następnej linii jego polskie tłumaczenie. Nazwa pliku będzie odwoływała się do kategorii słówek, które będą się znajdowały wewnątrz pliku. Na początek stworzymy plik, który będzie przechowywał słówka do nauki zwierząt.

Żeby w ogóle pracować z plikami tekstowymi musimy dodać do naszego projektu bibliotekę poleceniem include.

#include <fstream>

Tą linię dodajemy poniżej dodanej biblioteki iostream (poniżej pierwszej linii).

Teraz możemy utworzyć zmienna plikową typu fstream, która będzie nam służyła do odwoływania się do naszego pliku. Tak jak do zapisywania ciągów znaków wykorzystujemy zmienną o typie string tak dla plików tworzymy zmienną plikową o dowolnej nazwie typu fstream.

fstream moj_plik;

Następnie musimy podać komputerowi lokalizację naszego pliku i w jakim trybie chcemy do otworzyć, bo możemy otworzyć plik albo do zapisu albo do odczytu, w sumie logiczne jeżeli chcemy coś do pliku zapisać/lub dopisać albo chcemy coś z niego odczytać. Poniżej kod.

moj_plik.open("zwierzeta.txt",ios::out);

Funkcja open() służy do otwierania pliku. Pierwszy argument w nawiasach, u nas zwierzeta.txt, mówi o ścieżce dostępu do pliku, czyli o lokalizacji pliku na dysku. Tutaj jest tylko sama nazwa pliku więc code::blocks będzie szukał naszego pliku w katalogu z projektem. Co oznacza parametr ios::out. Słówko  ios znaczy input output stream czyli zaznaczamy, że chodzi nam o strumień danych, out oznacza, że ten strumień danych chcemy wyprowadzić z programu do pliku. Znak :: to operator zasięgu mówi tylko tyle, że funkcja out jest częścią składową klasy ios.

Funkcja open() przy próbie otwarcia pliku działa jeszcze w jeden sposób. Sprawdza ona czy w podanej lokalizacji (pierwszy parametr) znajduje się plik o wskazanej przez nas nazwie. Jeżeli tak to otworzy ten plik do zapisu, a jeżeli nie to najpierw utworzy taki plik i otworzy go do zapisu. Fajnie, nawet nie musimy się za bardzo martwić, co w sytuacji kiedy naszego pliku nie ma we wskazanej lokalizacji, funkcja open() załatwi to za nas.

Żeby „wrzucić” jakieś dane do pliku utworzymy sobie dwie zmienne pomocnicze które będą nam przechowywały słówko w języku angielskim i jego tłumaczenie po polsku podane przez użytkownika.

string slowkoen, slowkopl;

Zmienne tworzymy pomiędzy using namespace std; a funkcją main().

Teraz poprosimy użytkownika o wprowadzenie z klawiatury słówka angielskiego i jego polskiego tłumaczenia.

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

Czyli w zmiennych slowkoen i slowkopl mamy już słówka które chcemy zapisać w pliku. No to zapisujemy. Sprawa jest bardzo prosta.

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;

Na początku odwołujesz się do Twojego pliku poprzez zmienną plikową mój_plik a później „wyprowadzasz” z programu dane z tych zmiennych do pliku. Zwróć uwagę, że na końcu każdej linii wywołujemy funkcję endl() („end line”, czyli koniec linii). Gdybyśmy tego nie zrobili to wszystkie dane, angielskie i polskie słówko, zapisałoby się do pliku w jednej linii.

Dodatkowo możemy wyświetlić użytkownikowi komunikat o tym, że słówka zostały zapisane do pliku.

cout << "Slowka zapisane" << endl;

W momencie kiedy już nie będziemy korzystać z pliku należy zamknąć plik.

moj_plik.close();

Cały nasz kod będzie wyglądał jak poniżej.

 

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::out);

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;
    cout << "Slowka zapisane" << endl;

    moj_plik.close();

    return 0;
}

Po uruchomieniu program pyta użytkownika o podanie słówka angielskiego

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Po jego podaniu i potwierdzeniu enterem program pyta nas o polskie tłumaczenie podanego słówka.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

 

Po podaniu polskiego słówka, program wyświetla komunikat do użytkownika, że słówko jest dodane i kończy pracę. Oczywiście code::blocks wyświetla jeszcze swoje powiadomienie o czasie wykonania programu i czeka na wciśnięcie jakiegoś klawisza z klawiatury.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Widzimy, że w konsoli wszystko poszło zgodnie z planem, ale czy utworzyliśmy pliki i czy znajdują się w nim dane które wprowadziliśmy. Zobaczmy w katalogu z projektem czy istnieje plik o nazwie zwierzęta.txt.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Widzimy, że w katalogu naszego projektu znajduje się plik zwierzęta.txt czyli wszystko wygląda dobrze. Teraz zostało nam do sprawdzenia czy po otwarciu pliku dane które wysłaliśmy do pliku się zapisały.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Wszystko zadziałało prawidłowo, w pliku znajdują się dwie linie z zapisanymi słówkami. Ktoś jednak przytomnie spyta co się stanie w przypadku ponownego uruchomienia programu. Czy program dopisze dane do pliku czy je nadpisze. No to uruchamiamy program jeszcze raz.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Sprawdzamy co się dzieje w pliku.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Widzimy więc, że każde uruchomienie programu powoduje nadpisanie wcześniej wprowadzonych danych. Czy możemy coś z tym zrobić? Oczywiście, że tak. W miejscu gdzie określamy w jakim trybie otwieramy nasz plik musimy dodać parametr mówiący o tym, że chcemy dopisywać dane a nie zastępować. Wystarczy do linii

moj_plik.open("zwierzeta.txt",ios::out);

dodać parametr

moj_plik.open("zwierzeta.txt",ios::out | ios::app);

Sprawdźmy czy działa. Aktualnie w naszym pliku mamy dodanego psa. No to nie może zabraknąć także kota. Uruchamiamy program i dodajemy słówka cat / kot.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Sprawdźmy co się stało w naszym plik. Otwieramy plik zwierzęta.txt.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Bingo! Pod słówkami dotyczącymi psa dodały się dwie linie dotyczące kota. Oto właśnie nam chodziło. Widzisz więc, że zapisywanie danych do pliku tekstowego to nic trudnego. Finalnie nasz kod przybierze postać.

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::out | ios::app);

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;
    cout << "Slowka zapisane" << endl;

    moj_plik.close();

    return 0;
}

W kolejnych częściach kursu nauczysz się odczytywać te dane z pliku.


c++ kurs programowania obiektowego spis treści