Kurs podstawy programowania UMCS, zdjęcie laptopa.

PP – Laboratorium 14

Wejściówka

Zaprojektuj klasę Point, która posiada dwa prywatne pola typu zmiennoprzecinkowego x, y oraz publiczną metodę, która przyjmuje argument typu Point. Metoda powinna zwrócić odległość między punktem przekazanym w argumencie, a punktem, który wywołał metodę.

Rozwiązanie:
#include <cmath>

class Point {
    float x, y;
public:
    float distance(const Point &p) {return sqrt(pow(x - p.x, 2) + pow(y - p.y, 2));}
};

Zadanie 1 – napisy

Zaprojektuj klasę Person, która posiada trzy pola: liczbę naturalną – age, zmienną przechowującą tekst – name oraz liczbę zmiennoprzecinkową – height. Napisz funkcję entry, która zweryfikuje, czy dana osoba może skorzystać z kolejki górskiej o następujących ograniczeniach: height > 170, age > 18 oraz zwracać odpowiednią wartość logiczną. Napisz program w języku C++, który wykorzysta tą klasę i sprawdzi działanie funkcji entry.

Rozwiązanie:
#include <iostream>
#include <string>

class Person{
private:
    unsigned int age;
    float height;
    std::string name;
public:
    float get_height();
    unsigned int get_age();
    std::string get_name();
    Person(unsigned int age, float height, std::string name) : age(age), height(height), name(name) {}
};

std::string Person::get_name() {
    return this->name;
}

float Person::get_height() {
    return this->height;
}

unsigned int Person::get_age() {
    return this->age;
}

bool entry(Person *p) {
    return p->get_age() > 18 && p->get_height() > 170;
}

int main() {
    Person *p = new Person(21, 179, "Artur");
    std::cout << entry(p) << " " << p->get_name() << std::endl;
    delete p;
    return 0;
}
Omówienie:

Wraz z wprowadzeniem obiektowości w języku C++ powstało również narzędzie do pracy z tekstem – std::string. W celu skorzystania z std::string należy dołączyć plik nagłówkowy <string>.

Aby utworzyć zmienną, która przechowuje tekst używamy następującej składni:
std::string zmienna_tekstowa;

Oczywiście zmienną tekstową można zainicjalizować wartością początkową:
std::string zmienna_tekstowa = "przyklad";

Tekst podaje się zawsze w cudzysłowach, jeżeli nie nadano początkowej wartości tworzonej zmiennej tekstowej to zmienna będzie przechowywała tekst o zerowej liczbie znaków. Oczywiście zawsze można przypisać nową wartość zmiennej.


Wyświetlanie tekstu za pomocą strumienia std::cout wygląda dokładnie tak samo jak wypisywanie każdej innej zmiennej typu prostego:
std::cout << zmienna_tekstowa << std::endl;

Bardzo często zdarza się jednak, że funkcje wymagają tekstu zgodnego z zapisem wywodzącym się jeszcze z języka C – wówczas należy wywołać metodę należącą do std::string o nazwie c_str(). W praktyce przekłada się to na dopisanie .c_str() za nazwą zmiennej tekstowe:
std::cout << zmienna_tekstowa.c_str() << std::endl;
O ile w przypadku strumienia std::cout takie działanie jest niepotrzebne to np. przy użyciu funkcji printf zapis ten będzie konieczny.

Wczytywanie wyrazu ze standardowego wejścia jest tak samo proste jak w przypadku innych typów prostych:
std::cin >> zmienna_tekstowa;


Język C/C++ umożliwia bardzo wielu operacji na napisach i jest to temat bardzo rozbudowany. Poniżej zostanie przedstawione jedynie parę operacji.

Do łączenia, sklejania tekstów służy operator dodawania +:
std::string str1= "one";
std::string str2= " two";
std::string str3= str1 + str2;

dodatkowo można używać skróconego operatora +=:
std::string str1= "one";
str1 += "two";

jednakże nie można wykonać takiego zapisu:
str1 = "one" + " two";
Kompilator nie potrafi sklejać stałych łańcuchów znaków, dlatego też konieczne będzie rozbicie powyższego zapisu na kilka linijek.

Inną operacją, którą umożliwia język C/C++ to czyszczenie tekstu w zmiennej typu std::string:
zmienna_tekstowa = ""; lub zmienna_tekstowa.clear();

Zadanie 2

Napisz program w języku C++, który wczyta imię, nazwisko i wiek czterech osób. Następnie program powinien wyświetlić imię i nazwisko osoby, która jest najmłodsza. Gdy są dwie osoby w tym samym wieku powinien decydować porządek alfabetyczny nazwisk, a następnie imion.

Rozwiązanie:
#include <iostream>
#include <algorithm>
#include <string>

class Person {
private:
    std::string first_name;
    std::string last_name;
    unsigned int age;
public:
    std::string get_first_name();
    std::string get_last_name();
    unsigned int get_age();
    void set_first_name(std::string);
    void set_last_name(std::string);
    void set_age(unsigned int);
    Person() : Person("unknown", "unknown", 0) {}
    Person(std::string first_name, std::string last_name, unsigned int age)  : first_name(first_name), last_name(last_name), age(age) {}
};

std::string Person::get_first_name() {
    return this->first_name;
}

std::string Person::get_last_name() {
    return this->last_name;
}

unsigned int Person::get_age() {
    return this->age;
}

void Person::set_first_name(std::string first_name) {
    this->first_name = first_name;
}

void Person::set_last_name(std::string last_name) {
    this->last_name = last_name;
}

void Person::set_age(unsigned int age) {
    this->age = age;
}

bool comparator(Person *p1, Person *p2) {
    return p1->get_age() < p2->get_age() ||
            (p1->get_age() == p2->get_age() &&
             (p1->get_last_name() < p2->get_last_name() ||
              (p1->get_last_name() == p2->get_last_name() &&
                p1->get_first_name() < p2->get_first_name())));
}

int main() {
    std::string first_name, last_name;
    unsigned int age;
    Person **arr = new Person*[4];
    for(int i = 0; i < 4; ++i) {
        std::cin >> first_name >> last_name >> age;
        arr[i] = new Person(first_name, last_name, age);
        /*
        arr[i] = new Person();
        arr[i]->set_first_name(first_name);
        arr[i]->set_last_name(last_name);
        arr[i]->set_age(age);
        */
    }
    std::sort(arr, arr + 4, comparator);
    std::cout << arr[0]->get_first_name() << " " << arr[0]->get_last_name() << std::endl;
    for(int i = 0; i < 4; ++i) {
        delete arr[i];
    }
    delete[] arr;
    return 0;
}

Zadanie 3

Napisz program w języku C++, który odwóci napis wprowadzony przez użytkownika. Wykorzystaj do tego zmienną typu std::string.

Rozwiązanie:
#include <iostream>
#include <string>

int main() {
    std::string str;
    std::cin >> str;
    std::cout << std::string(str.rbegin(), str.rend()) << std::endl;
    return 0;
}

Zadanie 4

Napisz program w języku C++, który zmienia wszystkie małe litery, w tekście wprowadzonym przez użytkownika, na wielkie litery.

Rozwiązanie:
//Version 1.0
#include <iostream>
#include <string>
#include <algorithm>

int main() {
    std::string text;
    getline(std::cin, text);
    std::transform(text.begin(), text.end(), text.begin(), ::toupper);
    std::cout << text << std::endl;
    return 0;
}
//Version 2.0
#include <iostream>
#include <cstring>
#include <string>

int main() {
    std::string text;
    getline(std::cin, text);
    char * cstr = new char[text.length() + 1];
    std::strcpy(cstr, text.c_str());
    for(int i = 0; cstr[i]; ++i)
        if(cstr[i] >= 'a' && cstr[i] <= 'z')
            cstr[i] -= 32;
    text.clear();
    text = cstr;
    delete[] cstr;
    std::cout << text << std::endl;
    return 0;
}
Omówienie:

Funkcja getline to funkcja, która przyjmuje dwa lub trzy argumenty. Pierwszy argument przyjmuje strumień wejściowy z którego dane mają zostać wczytane (np. std::cin, std::ifstream), zaś w drugim argumencie przekazujemy zmienną tekstową (std::string) do której ma zostać wczytany tekst. Ostatnim argumentem jest znak, który określa moment, kiedy operacja wczytywania zostanie zatrzymana.

W przypadku użycia funkcji z dwoma argumentami wyodrębniane są znaki z pierwszego argumentu do drugiego, do momentu znalezienia ogranicznika znaku nowej linii '\n' (lub końca pliku), zaś dla wersji trójrgumentowej do napotkania znaku przekazanego w trzecim argumencie. Należy mieć na uwadze, że ten ostatni znak – ogranicznik nie zostanie wczytany, a kolejne wywołanie operacji zacznie się od znaku za tym ogranicznikiem.

Zadanie 5 – obsługa plików

Napisz program w języku C++, który wczyta z pliku tekstowego wszystkie słowa do tablicy napisów. Zakładamy, że słowa w pliku tekstowym nie są dłuższe niż 99 znaków.

Rozwiązanie:
#include <cstdio>
#include <string>

void print_all_file_words(const char *filename) {
    char temp_str[100] = {};
    std::string temp_arr[100];
    int count = 0;
    FILE *in;
    if((in = fopen(filename, "rt")) == NULL) {
        printf("Blad!");
    } else {
        while(fscanf(in, "%99s", temp_str) != EOF) {
            temp_arr[count++] = std::string(temp_str);
        }
        fclose(in);
    }

    for(int i = 0; i < count; ++i)
        printf("%s\n", temp_arr[i].c_str());
}

int main() {
    print_all_file_words("result.bin");
    return 0;
}
Omówienie:

W tym kursie omówimy dwa sposoby obsługi plików, za pomocą strumieni i za pomocą obiektu, uchwytu na plik: FILE*.

Zaczniemy od tego drugiego, aby otworzyć plik używamy funkcji fopen(), która przyjmuje w argumencie nazwę pliku (lub ścieżkę do pliku) oraz tryb dostępu do pliku.

const char *filename = "/file/path";
FILE *in = fopen(filename, "rt"));

Pierwszy argument powinien być zgodny ze specyfikacją nazwy pliku działającego środowiska. Dodatkowo, jeśli system obsługuje ścieżki do pliku, możemy taką podać. Ścieżki do pliku mogą być względne oraz bezwzględne. Ścieżka względna to taka, która nie zawiera pełnej ścieżki do pliku. Tym samym ścieżką względną będzie np. podanie samej nazwy pliku wraz z jego rozszerzeniem, bądź podanie ścieżki względem katalogu roboczego aplikacji. Ścieżka bezwzględna określa natomiast pełną ścieżkę do pliku i zaczyna się od litery dysku w przypadku Windows lub od symbolu katalogu głównego / w systemach typu Unix (wliczając Linux i MacOS), a kończy się na pełnej nazwie pliku. Dodatkowo w przypadku podawania ścieżki do pliku pod systemem Windows należy pamietać o podwyjnym użyciu znaku \ w ścieżce. Przykłady ścieżek dla różnych systemów:
Windows – C:\\Users\\username\\Documents\\file.cpp,
Unix – /home/username/Documents/file.cpp.

Drugi argument może przyjąć jedną z poniższych wartości:
"r" – Otwórz plik do odczytu, operacji wejściowych (plik musi istnieć).
"w" – Otwórz plik do zapisu, operacji wyjściowych. W sytuacji, gdy plik o wskazanej w pierwszym argumencie nazwie nie istnieje, zostanie stworzony nowy pusty plik, a w przeciwnym wypadku otwarty zostanie plik, który już istnieje ale jego zawartość zostanie odrzucona.
"a" – Otwórz plik do zapisu na jego końcu / operacji wyjściowych. Operacje zapiszą dane na końcu pliku, a jeśli taki plik nie istnieje, to zostanie utworzony.
"r+" – Otwórz plik do odczytu i aktualizacji, operacje wejściowe i wyjściowe (plik musi istnieć).
"w+" – Utwórz pusty plik i otwórz go do aktualizacji, operacje wejściowe i wyjściowe. Jeśli plik o takiej nazwie już istnieje, jego zawartość jest odrzucona, a plik traktowany jest jako nowy, pusty plik.
"a+" – Otwórz plik do aktualizacji, zarówno dla operacji wejściowych, jak i wyjściowych z tym, że wszystkie operacje wyjściowe zapisują dane na końcu pliku. Tutaj warto zaznaczyć, że operacje zmiany położenia tj. fseek, fsetpos, rewind (które poznamy w dalszej części tego kursu) wpływają na operacje wejściowe, ale operacje wyjściowe przesuwają pozycję z powrotem na koniec pliku. W sytuacji, gdy plik o podanej nazwie nie istnieje, to zostanie on stworzony.
Do powyższych możemy dodać dodatkowo literkę "t" lub "b", które kolejno otwierają plik w trybie tekstowym (tryb domyślny) lub binarnym.

Odczytywanie danych z pliku tekstowego jest analogiczne do obsługi standardowego wejścia za pomocą funkcji scanf z tym, że dla plików używamy funkcji fscanf. Mamy dostępne następujące funkcje odczytu: fscanf, fread i następujące funkcje zapisu: fprintf, fwrite. Obu możemy używać w trybach otwarcia tekstowym i binarnym. Różnica między fscanf/fprintf i fread/fwrite jest w tym, że te pierwsze operują na danych tekstowych formatowanych, zaś te drugie na nieformatowanych.

Oczywiście trzeba pamiętać o zamknięciu pliku: fclose(in), gdy zakończymy na nim prace. Warto również dodać, że plik kończy się specjalnym znakiem EOF. Podczas przeglądania pliku możemy to wykorzystać, sprawdzając czy dotarliśmy do końca pliku lub użyć w tym celu funkcji feof, która zwraca wartość rożną od 0, gdy strumień osiągnął swój koniec lub 0 w przeciwnym wypadku.

Zadanie 6

Napisz funkcję w języku C++, która przyjmuje ścieżkę do pliku tekstowego. W pliku tekstowym znajdują się posortowane rosnąco liczby całkowite oddzielone pojedynczymi spacjami. Funkcja powinna wczytać te liczby do tablicy i znaleźć w nich wartość minimalną i maksymalną.

Rozwiązanie:
#include <cstdio>

int find_min(int *arr, int n) {
    int result = arr[0];
    for(int i = 1; i < n; ++i)
        if(arr[i] < result)
            result = arr[i];
    return result;
}

int find_max(int *arr, int n) {
    int result = arr[0];
    for(int i = 1; i < n; ++i)
        if(arr[i] > result)
            result = arr[i];
    return result;
}

void min_max_value(const char *filename) {
    int arr[512] = {0}, count = 0, min, max, tmp;
    FILE *in;

    if((in = fopen(filename, "rt")) == NULL) {
        printf("Blad!");
    } else {
        while(fscanf(in, "%d", &tmp) != EOF) {
            arr[count++] = tmp;
        }
        fclose(in);
    }

    //W treści zadania wyraźnie mamy powiedziane, że liczby są posortowane rosnąco. W związku z tym wystarczy:
    printf("%d %d\n", arr[0], arr[count - 1]);

    min = find_min(arr, count);
    max = find_max(arr, count);
    printf("%d %d\n", min, max);
}

int main() {
    min_max_value("result.bin");
    return 0;
}

Zadanie 7

Napisz program w języku C++, który zapisze do pliku 10 dowolnych liczb całkowitych. Do obsługi błędów użyj funkcji perror.

Rozwiązanie:
#include <cstdio>

int main() {
    FILE *out = NULL;

    if((out = fopen("result.bin", "wb")) == NULL) {
        perror("fopen() failed");
        return 1;
    } else {
        int arr[10];
        for(int i = 0; i < 10; ++i) arr[i] = i;
        fwrite(arr, sizeof(int), 10, out);
        fclose(out);
    }

    return 0;
}

Zadanie 8

Napisz program w języku C++, który usunie plik stworzony w poprzednim zadaniu.

Rozwiązanie:
#include <cstdio>

int main () {
    if(remove("result.bin") != 0) perror("Błąd!");
    else printf("Usunięto!");
    return 0;
}

Zadanie 9

Zaprojektuj strukturę Car, która posiada dwa pola: register_number, będący jednobajtową liczbą całkowitą bez znaku (0 - 255), brand_name, będąca maksymalnie 15 znakowym napisem. Następnie napisz funkcję w języku C++, która przyjmuje tablicę zmiennych typu Car, jej rozmiar oraz stały napis będący nazwą pliku, do którego funkcja powinna zapisać przekazaną tablicę.

Rozwiązanie:
#include <cstdio>

typedef unsigned char uchar;
struct Car {
    uchar register_number;
    char brand_name[16];
};

void f(Car arr[], int n, const char *filename) {
    FILE *out = NULL;
    if((out = fopen(filename, "wb")) == NULL) {
        perror("Blad!");
    } else {
        fwrite(arr, sizeof(Car), n, out);
        fclose(out);
    }
}

int main() {
    Car arr[5] = {1, "Volvo", 2, "Opel", 3, "BMW", 4, "Tesla", 5, "Fiat"};
    f(arr, 5, "result.bin");
    return 0;
}

Zadanie 10

Napisz funkcję w języku C++, która przyjmuje ścieżkę do pliku tekstowego. W pliku tekstowym znajdują się liczby całkowite oddzielone pojedynczymi spacjami. Funkcja powinna wczytać te liczby zwiększyć o jeden i ponownie zapisać do pliku. Do obsługi plików użyj strumieni.

Rozwiązanie:
#include <iostream>
#include <fstream>

bool f(const char *filename) {
    int arr[512], temp, count = 0;
    std::ifstream input_file;
    input_file.open(filename);
    if(!input_file.good()) return false;
    while(input_file >> temp) {
        arr[count++] = temp;
    }
    input_file.close();

    std::ofstream output_file;
    output_file.open(filename);
    if(!output_file.good()) return false;
    for(int i = 0; i < count; ++i) {
        output_file << ++arr[i] << " ";
    } output_file << "\n";
    output_file.close();

    return true;
}

int main() {
    if(!f("result.bin")) 
        std::cout << "Nie udalo sie otworzyc pliku o podanej nazwie" << std::endl;
    return 0;
}
Omówienie:

Aby mieć możliwość korzystania z narzędzi do obsługi plików, dostarczonych wraz ze standardem C++, należy na początku kodu źródłowego dołączyć plik nagłówkowy fstream, który umożliwi nam odczytywanie i zapisywanie do pliku. Następnym krokiem jest utworzenie obiektu, który umożliwia komunikację ze wskazanym plikiem. Obiekt ten tworzy się analogicznie do zmiennych. Podstawowym typem danych jest std::fstream, dla którego podczas otwierania pliku należy określić tryb obsługi pliku np. std::fstream::in, std::fstream::out, tym samym uzysakmy następującą operację:
f.open(filename, std::fstream::in | std::fstream::out);

Innymi typami danych będzie std::ifstream lub std::ofstream, w zależności od tego, czy chcemy odczytywać dane z pliku, czy je do niego zapisywać. std::ifstream służy do odczytywania danych, zaś std::ofstream do ich zapisu.

Oczywiście, jak w przypadku uchwytu FILE, tak i dla składni bardziej naturalnej w języku C++, należy pamiętać o zamknięciu pliku. Do tego celu służy metoda close.


Odczytywanie tekstu z pliku sprowadza się do użycia operatora >> do pobierania danych ze strumienia, bądź zastosowania funkcji getline. Aby zapisywać dane do pliku używamy operatora << do wstawienia danych do strumienia. Do obsługi danych nieformatowanych, ich zapisywania i odczytywania, możemy używać następujących metod write i read.

Zadanie 11

Napisz funkcję w języku C++, która przyjmuje ścieżkę do pliku tekstowego. W pliku tekstowym znajdują się posortowane rosnąco liczby całkowite oddzielone pojedynczymi spacjami. Funkcja powinna wstawić dodatkowe 5 losowych liczb z przedziału domkniętego, gdzie min to minimalna wartość w pliku, zaś max maksymalna. Wstawiane liczby nie powinny zachwiać kolejności liczb całkowitych w pliku. Tym samym nowe liczby powinny być wprowadzone w odpowiednie miejsce tak, aby wszystkie liczby w pliku dalej były posortowane rosnąco.

Rozwiązanie:
#include <iostream>
#include <fstream>
#include <ctime>
#include <cstdlib>
#include <algorithm>

bool compare(int a, int b) {
    return a < b;
}

bool f(const char *filename) {
    int arr[512], count = 0, min, max, tmp;
    std::ifstream input_file;
    input_file.open(filename);
    if(!input_file.good()) return false;
    while(input_file >> tmp) {
        arr[count++] = tmp;
    }
    input_file.close();

    min = arr[0];
    max = arr[count - 1];
    for(int i = 0; i < 5; ++i) 
        arr[count++] = min + rand() / (RAND_MAX / (max - min + 1));
    std::sort(arr, arr + count, compare);

    std::ofstream output_file;
    output_file.open(filename);
    if(!output_file.good()) return false;
    for(int i = 0; i < count; ++i) {
        output_file << arr[i] << " ";
    } output_file << "\n";
    output_file.close();

    return true;
}

int main() {
    srand(time(0));
    if(!f("result.bin")) 
        std::cout << "Nie udalo sie otworzyc plik o podanej nazwie." << std::endl;
    return 0;
}

Zadanie 12

Napisz funkcje w języku C++, która przyjmuje ścieżkę do pliku tekstowego oraz 5-cio literowe słowo wczytane od użytkownika. Funkcja powinna znaleźć w pliku ***** i zamienić ten ciąg gwiazdek na słowo wczytane od użytkownika. Np. Ala ma *****. -> Ala ma rybke.

Rozwiązanie:
#include <iostream>
#include <fstream>

bool f(const char *filename, const char *str) {
    int pos = -1;
    std::string line;
    std::fstream f;

    f.open(filename, std::fstream::in | std::fstream::out);
    if(!f.good()) return false;
    while(getline(f, line)) {
        pos = line.find("*****");
        if(pos >= 0) {
            f.seekg(pos + f.tellg() - line.length(), std::ios::beg);
            f << str;
        }
    }
    f.close();
    return true;
}

int main() {
    if(!f("result.bin", "rybke")) 
        std::cout << "Nie udalo sie otworzyc pliku o podanej nazwie." << std::endl;
}
Omówienie:

Oczywiście język C/C++ umożliwia nam przejście do dowolnego miejsca w pliku, w tym celu używamy funkcji fseek i odpowiednich makrodefinicji: SEEK_SET – początek pliku, SEEK_CUR – aktualna pozycja strumienia, SEEK_END – koniec pliku. Należy dodać, że podczas przeglądania pliku aktualna pozycja strumienia się zmienia!

Aby dostać się do odpowiedniego miejsca w pliku możemy zrobić to za pomocą poniższej składni:

fseek(file, -4, SEEK_CUR);

Powyższy kod cofnie nas o 4 znaki, od aktualnej pozycji.


Oczywiście operacje na strumieniach, również oferują nam takie możliwości. W klasie std::ifstream znajduje się dwie metody o nazwie seekg, które służą do ustawienia pozycji od której będą wczytywane z dane z pliku. Pierwsza wersja metody seekg przyjmuje jeden argument za pomocą którego określamy pozycję od której odczytywane będą dane z pliku. Pozycja ta musi być wyrażana względem początku pliku. W praktyce oznacza to, że wskazuje po prostu numer znaku od którego rozpoczęte zostanie czytanie pliku.

std::ifstream file;
file.seekg(10);

Druga wersja metody seekg posiada dwa argumenty. Pierwszym argumentem określa się ’o ile przesunie się pozycja odczytu’, natomiast drugi argument określa ’względem jakiej pozycji przesunąć pozycję odczytu’. Argument pierwszy jest więc liczbą całkowitą (może być to wartość ujemna), natomiast drugi argument może przyjąć jedną z trzech wartości: std::ios::beg – przesunięcie pozycji jest wyrażone względem początku pliku, std::ios::cur – przesunięcie pozycji jest wyrażane względem aktualnej pozycji, std::ios::end – przesunięcie pozycji jest wyrażone względem końca pliku. Przykłady:

//Koniec pliku:
file.seekg(0, std::ios::end);
//3 w lewo od aktualnej pozycji:
file.seekg(-3, std::ios::cur);
//określenie pozycji na 6 znak od początku:
plik.seekg(5, ios::beg);
plik.seekg(5);

Utworzony obiekt typu std::ifstream, który wykorzystujemy do pracy z plikiem umożliwia również odczytywanie aktualnej pozycji odczytu danych z pliku. Do tego celu służy metoda tellg, która nie przyjmuje żadnego argumentu, ale za to zwraca wartość, która określa aktualną pozycję odczytu danych z pliku. Zwracana pozycja jest wyrażona zawsze względem początku pliku. Przykład:

std::streampos pos = plik.tellg();
std::cout << pos << std::endl;

Warto wspomnieć, że metodę tellg można wykorzystać przy określaniu pozycji odczytu danych z pliku:

//to samo
plik.seekg(8, ios::cur);
plik.seekg(plik.tellg() + 8);

Zadanie 13

Zmodyfikuj poprzednią funkcję tak aby możliwe było wprowadzenie dowolnie długiego napisu w miejsce gwiazdek. Sekwencja pięciu gwiazdek ***** może wystąpić w pliku więcej niż jeden raz.

Rozwiązanie:
#include <iostream>
#include <fstream>
#include <string>

std::string get_file(std::ifstream &in) {
    std::string content;
    for(char ch; in.get(ch); content.push_back(ch));
    return content;
}

void find_and_replace(std::string &file_content, const std::string& src, const std::string& dst) {
    size_t pos = file_content.find(src);
    while(pos != std::string::npos) {
        file_content.replace(pos, src.length(), dst);
        pos = file_content.find(src , pos);
    }
}

int main() {
    std::ifstream input_file("result.bin");
    if(input_file.good()) {
        std::string content = get_file(input_file);
        find_and_replace(content, "*****", "dowolne");
        input_file.close();

        std::ofstream output_file("result.bin");
        if(output_file.good()) {
            output_file << content;
            output_file.close();
        }
    }
    return 0;
}

Zadanie 14

Napisz program w języku C++, który odczyta i wyświetli dane z pliku o następującej strukturze:
Ala ma X kotow w nastepujacym wieku:
DATA

gdzie X to ilość kotów w tablicy DATA. DATA to wartości kolejnych elementów tablicy zapisane binarnie. Każdy element tablicy DATA jest reprezentowany za pomocą liczby całkowitej. Program powinien wyświetlić wiek wszystkich kotów.

Rozwiązanie:
#include <iostream>
#include <sstream>
#include <fstream>

int extract_cats_count(std::string str)  {
    std::stringstream ss;
    ss << str;

    std::string temp;
    int found;
    while(!ss.eof()) {
        ss >> temp;
        if(std::stringstream(temp) >> found)
            std::cout << found << " ";
        temp = "";
    }
    return found;
}

int main()  {
    std::string line;
    std::ifstream input_file("result.bin");
    if(!input_file.good()) 
        return -1;

    getline(input_file, line);
    int n = extract_cats_count(line);

    int *buff = new int[n];
    input_file.read((char*)buff, sizeof(int) * n);
    for(int i = 0; i < n; ++i) 
        std::cout << buff[i] << " ";
    std::cout << std::endl;

    input_file.close();
    delete[] buff;
    return 0;
}
Omówienie:

Wyróżniamy jeszcze jeden rodzaj strumieni – std::stringstream. Dzięki niemu jesteśmy w stanie operować na napisach tak, jak na zwykłym strumieniu. Wyobraźmy sobie sytuację, gdy musimy zamienić liczbę całkowitą na napis. Język C umożliwiał nam dokonywanie takich operacji za pomocą funkcji sprintf(), bądź niestandardowej funkcji itoa(). Jednak zaprezentowane poniżej rozwiązanie jest o wiele czytelniejsze:

#include <iostream>
#include <sstream>

int main ()
{
   long x;   // Zmienna do przechowania liczby
   std::string napis;   // Zmienna do przechowania napisu
   std::stringstream ss;  // Strumień do napisów

   std::cout << "Podaj dowolna liczbe calkowita: ";
   std::cin >> x;

   ss << x;   // Do strumienia 'wysyłamy' podaną liczbę
   napis = ss.str();   // Zamieniamy zawartość strumienia na napis

   std::cout << "Dlugosc napisu wynosi " << napis.size() << " znakow." << std::endl;
   return 0;
}

Zadanie 15

Napisz program w języku C++, który umożliwi użytkownikowi wprowadzenie wiadomości zawierającej informacji o jego imieniu, wieku, wzroście i wadze w następującym formacie np. Test 12 182 82.2. Odczytaj informacje z tak przekazanej wiadomości i zapisz je w odpowiednich zmiennych. Następnie program powinien wyświetlić te informację w następujący sposób:
  Imię (dane w tej linii powinny być przesunięte w prawą stronę – wcięcie w tekście)
Wiek (w systemie szesnastkowym)
Wzrost (w systemie dziesiętnym)
Waga (z dokładnością do 3 miejsc po przecinku)

Rozwiązanie:
#include <iostream> //cout, dec, hex, fixed
#include <sstream> //isstream
#include <iomanip> //setw, setprecision

int main () {
   std::string name;
   int age, height;
   float weight;
   std::istringstream isstream("Emil 27 169 72.8");
   isstream >> name >> age >> height >> weight;

   if(isstream.fail())
    std::cout << "Error in parsing" << std::endl;
   else 
   std::cout << std::setw(10) << name << std::endl << std::hex << age << std::endl << std::dec << height << std::endl << std::fixed << std::setprecision(3) << weight << std::endl;

   return 0;
}

Zadanie 16*

Mateusz, młody programista, spędził w Lublinie kilka dni. Przykładny i skrupulatny student uczestniczył we wszystkich zajęciach akademickich, aby pozyskać nową wiedzę. Nasz bohater zmęczony całym tygodniem nauki oraz natłokiem obowiązków zawodowych, zaplanował sobie wycieczkę krajoznawczą na weekend. W związku z piękną pogodą wybranym przez Mateusza środkiem lokomocji był jego rower Ukraina, zaś celem podróży nieodległa miejscowość Łęczna. W Łęcznej zainteresowała go „Dolina dinozaurów”, którymi Mateusz pasjonował się całe swoje dzieciństwo. Niewątpliwie łęczyńska dolina dinozaurów jest ciekawym miejscem. Jej pomysłodawcą i twórcą jest lokalny rzeźbiarz, a znajdującą się w Podzamczu k. Łęcznej dolinę tworzy 8 betonowych rzeźb wielkości od 2 do 8 m. Mateusz zwiedzając „Dolinę dinozaurów” zobaczył dziwne, niezidentyfikowane znaki, napisane na piachu, które jak się okazało były ciągami liczb. Niestety żaden ze zwiedzających nie miał pojęcia, kto je tutaj zostawił. Zaistniała sytuacja bardzo zaintrygowała naszego bohatera, który przystąpił do badań nad zagadkowymi ciągami.

Mateusz po wielu próbach przekształcenia i usystematyzowania ciągów liczb doszedł do wniosku, że kolejne liczby ciągu tworzą pary (pierwsza liczba z drugą, trzecia z czwartą itd.), odpowiadające wysokości oraz grubości figury. Pary (wysokość, grubość) po niewielkim przekształceniu tworzą posegregowany ciąg liczb, z którego łatwo odczytać grubości rzeźb na odpowiedniej wysokości. Aby otrzymać tak uporządkowany ciąg nasz zdolny student doszedł do następujących wniosków. W związku z tym, że pary rzadko mają taką samą pierwszą współrzędną Mateusz zauważył, że sprytnym sposobem będzie posegregowanie serii par rosnąco względem właśnie tej współrzędnej. Dodatkowo w sytuacji, gdy pierwsze współrzędne są równe, kolejność determinowana jest przez największy nietrywialny dzielnik drugiej współrzędnej (jeśli liczba nie ma takiego dzielnika to uznajemy, że jest równy jeden). Ta para, której druga współrzędna ma większy dzielnik jest wcześniej w ciągu. Pary, których pierwsze współrzędne oraz największe dzielniki drugich współrzędnych są równe powinny występować w ciągu wynikowym w kolejności takiej jak w oryginalnym ciągu.

Niestety w związku z tym, że Mateusz musi przygotować się do pracy, nie ma czasu by napisać program, który przekształci spisane przez niego ciągi liczbowe. Nasz bohater zwrócił się do Ciebie o pomoc w zaimplementowaniu tego algorytmu.

Algorytm przyjmuje na wejściu dowolną liczbę naturalną N, a następnie N liczb naturalnych D będących długościami ciągów. Po każdej wartości D program powinien przyjmować D liczb naturalnych tworzących jeden ciąg liczbowy do przekształcenia.

Przykład:
input:
3
6
5 6 7 8 9 10
8
31 22 91 101 31 99 8 14
24
11 65 22 19 5 7 8 4 90 90 22 4 37 81 92 13 22 102 31 22 11 101 74 11
output:
5 6 7 8 9 10
8 14 31 99 31 22 91 101
5 7 8 4 11 65 11 101 22 102 22 4 22 19 31 22 37 81 74 11 90 90 92 13

Rozwiązanie:
#include <iostream>
#include <algorithm>

struct Coordinates {
    unsigned int x;
    unsigned int y;
};

unsigned int find_divider(unsigned int number){
    for(unsigned int i = number / 2; i > 1; --i)
        if(number % i == 0)
            return i;
    return 1;
}

bool compare(Coordinates *p1, Coordinates *p2) {
    return !(p1->x > p2->x || (p1->x == p2->x && find_divider(p1->y) < find_divider(p2->y)));
}

void print_result(Coordinates **arr, unsigned int n) {
    for(unsigned int j = 0; j < n; j++)
        std::cout << arr[j]->x << " " << arr[j]->y << " ";
    std::cout << "\n";
}

void process_data(Coordinates **arr, unsigned int n) {
    unsigned int d;
    for(unsigned int i = 0; i < n; i++) {
        std::cin>>d;
        d /= 2;
        arr = new Coordinates*[d];
        for(unsigned int j = 0; j < d; j++) {
            arr[j] = new Coordinates();
            std::cin >> arr[j]->x >> arr[j]->y;
        }
        std::sort(arr, arr + d, compare);
        print_result(arr, d);
    }
}

int main() {
    unsigned int n;
    Coordinates **arr;
    std::cin>>n;
    process_data(arr, n);
    return 0;
}

Zadanie 17

Napisz funkcję w języku C++, która przyjmie obiekt std::vector oraz dwie wartości całkowite a i b. Funkcja powinna zwrócić obiekt std::vector zawierający liczby z wektora wejściowego znajdujące się w obustronnie otwartym zakresie (a, b). Napisz program w języku C++, który przetestuje działanie tej funkcji.

Rozwiązanie:
#include <iostream>
#include <vector>

void print(std::vector<int>& v) {
    for(unsigned int i = 0; i < v.size(); ++i)
        std::cout << v.at(i) << " ";
    std::cout << "\n";

}

std::vector<int> get_rearrange_vector(const std::vector<int>& v, const int& a, const int& b) {
    std::vector<int> result;
    for(int value : v)
        if(value >= a && value <= b)
            result.push_back(value);
    return result;
}

int main() {
    int min = 3, max = 8;
    std::vector<int> v = {8, 4, 5, 9, 1, 2, 3};
    std::vector<int> result = get_rearrange_vector(v, min, max);
    print(result);
    return 0;
}

Zadanie 18

W języku C++ stwórz funkcję write_array(), która zapisze podaną w argumencie tablicę liczb zmiennoprzecinkowych do pliku (nazwa podana w argumencie funkcji) w postaci binarnej (nieformatowanej), gdzie każdy element tablicy jest kodowany na czterech bajtach. Stwórz analogiczną funkcję read_array(), która wczyta z takiego pliku (nazwa podana w argumencie funkcji) dane do przekazanej w argumencie tablicy liczb zmiennoprzecinkowych. Funkcja powinna wczytać nie więcej elementów niż zadeklarowana wielkość tablicy i zwrócić rzeczywistą ilość wczytanych elementów.

Rozwiązanie:
#include <iostream>
#include <fstream>

void write_array(const float arr[], size_t size, const std::string& filename) {
    std::ofstream file(filename, std::ios::binary);
    file.write((const char *)arr, sizeof(float) * size);
    file.close();
}

size_t read_array(float arr[], size_t size, const std::string& filename) {
    std::ifstream file(filename, std::ios::binary);
    file.read((char *)arr, sizeof(float) * size);
    size_t num_elements = file.gcount() / sizeof(float);
    file.close();
    return num_elements;
}

int main() {
    const size_t result_n = 4;
    float result_arr[result_n], arr[] = {1.2f, 2.3f, 3.4f, 4.5f};
    size_t size = sizeof(arr) / sizeof(float);
    size_t file_num_elements = 0;
    std::string filename = "data.bin";

    write_array(arr, size, filename);
    file_num_elements = read_array(result_arr, result_n, filename);

    std::cout << file_num_elements << std::endl;
    for (size_t i = 0; i < result_n; i++) std::cout << result_arr[i] << " ";
    std::cout << std::endl;

    return 0;
}

Zadanie 19

W języku C++ stwórz prostą strukturę do obsługi macierzy. Zdefiniuj funkcje write_mat() i read_mat() do zapisu/odczytu macierzy do/z pliku tekstowego (formatowanego). Plik tekstowy ma być sformatowany w ten sposób, że w pierwszym wierszu są dwie liczby całkowite: szerokość i wysokość macierzy a w następnych liniach są zapisane kolejno elementy macierzy rozdzielone spacją w postaci liczb zmiennoprzecinkowych.
Przykładowy plik wejściowy:
5 3
1.0 2.0 3.0 4.0 5.0
6.0 7.0 8.0 9.0 0.0
3.0 5.0 7.0 9.0 1.0

Rozwiązanie:
#include <iostream>
#include <fstream>
#include <cstring>

struct Matrix {
    float *data;
    int width;
    int height;
    Matrix() : width(1), height(1) { data = new float[width * height]; }
    Matrix(int width, int height) : width(width), height(height) { data = new float[width * height]; }
    Matrix(float data[], int width, int height) : width(width), height(height) {
        this->data = new float[width * height];
        std::memcpy(this->data, data, sizeof(float) * width * height);
    }
    ~Matrix() { delete[] data; }
    void print() {
        for (int i = 0; i < height; ++i) {
            for (int j = 0; j < width; ++j)  std::cout << data[i * width + j] << " ";
            std::cout << std::endl;
        }
    }
};

void write_mat(const Matrix& matrix, const std::string& filename) {
    std::ofstream file(filename);

    file << matrix.width << " " << matrix.height << std::endl;
    for (int i = 0; i < matrix.height; ++i) {
        for (int j = 0; j < matrix.width; ++j)  file << matrix.data[i * matrix.width + j] << " ";
        file << std::endl;
    }

    file.close();
}

Matrix read_mat(const std::string& filename) {
    std::ifstream file(filename);
    int width, height;
    file >> width >> height;
    Matrix matrix(width, height);

    for (int i = 0; i < matrix.height; ++i)
        for (int j = 0; j < matrix.width; ++j)
            file >> matrix.data[i * matrix.width + j];

    file.close();
    return matrix;
}

int main() {
    const int n = 5, m = 3;
    float data[n * m] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 0.0f, 3.0f, 5.0f, 7.0f, 9.0f, 1.0f };
    Matrix output_matrix(data, n, m);
    output_matrix.print();

    std::string filename = "matrix.txt";
    write_mat(output_matrix, filename);

    Matrix input_matrix = read_mat(filename);
    input_matrix.print();

    return 0;
}

Zadanie 20

W języku C++ zdefiniuj funkcje write_bin_mat() i read_bin_mat() do zapisu/odczytu macierzy do/z pliku binarnego (nieformatowanego). Plik binarny ma być skonstruowany w ten sposób, że pierwsze dwa bajty kodują szerokość macierzy, a dwa kolejne wysokość macierzy, a następnie jest sekwencja wszystkich elementów macierzy kodowanych na czterech bajtach każdy (liczba typu float).

Rozwiązanie:
#include <iostream>
#include <fstream>
#include <cstring>

struct Matrix {
    float *data;
    int width;
    int height;
    Matrix() : width(1), height(1) { data = new float[width * height]; }
    Matrix(int width, int height) : width(width), height(height) { data = new float[width * height]; }
    Matrix(float data[], int width, int height) : width(width), height(height) {
        this->data = new float[width * height];
        std::memcpy(this->data, data, sizeof(float) * width * height);
    }
    ~Matrix() { delete[] data; }
    void print() {
        std::cout << width << " " << height << std::endl;
        for (int i = 0; i < height; ++i) {
            for (int j = 0; j < width; ++j)  std::cout << data[i * width + j] << " ";
            std::cout << std::endl;
        }
    }
};

void write_mat(const Matrix& matrix, const std::string& filename) {
    std::ofstream file(filename);

    file << matrix.width << " " << matrix.height << std::endl;
    for (int i = 0; i < matrix.height; ++i) {
        for (int j = 0; j < matrix.width; ++j)  file << matrix.data[i * matrix.width + j] << " ";
        file << std::endl;
    }

    file.close();
}

Matrix read_mat(const std::string& filename) {
    std::ifstream file(filename);
    int width, height;
    file >> width >> height;
    Matrix matrix(width, height);

    for (int i = 0; i < matrix.height; ++i)
        for (int j = 0; j < matrix.width; ++j)
            file >> matrix.data[i * matrix.width + j];

    file.close();
    return matrix;
}

void write_mat_bin(const Matrix& matrix, const std::string& filename) {
    std::ofstream file(filename, std::ios::binary);
    file.write((const char *)&matrix.width, sizeof(short));
    file.write((const char *)&matrix.height, sizeof(short));
    file.write((const char *)matrix.data, sizeof(float) * matrix.width * matrix.height);
    file.close();
}

Matrix read_mat_bin(const std::string& filename) {
    std::ifstream file(filename, std::ios::binary);
    short width, height;
    file.read((char *)&width, sizeof(short));
    file.read((char *)&height, sizeof(short));
    Matrix matrix(width, height);
    file.read((char *)matrix.data, sizeof(float) * width * height);
    file.close();
    return matrix;
}

int main() {
    const int n = 5, m = 3;
    float data[n * m] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 0.0f, 3.0f, 5.0f, 7.0f, 9.0f, 1.0f };
    Matrix output_matrix(data, n, m);
    output_matrix.print();
    std::cout << std::endl;


    std::string filename = "matrix.txt";
    write_mat(output_matrix, filename);

    Matrix input_matrix = read_mat(filename);
    input_matrix.print();
    std::cout << std::endl;

    filename = "matrix";
    write_mat_bin(output_matrix, filename);

    input_matrix = read_mat_bin(filename);
    input_matrix.print();

    return 0;
}

Zadanie 21

W języku C++ zdefiniuj strukturę City opisującą miasto i zawierającą składowe: nazwa miasta (string), ilość mieszkańców (uint), dwie współrzędne geograficzne (2x double). Zdefiniowany jest plik tekstowy typu csv zawierający listę takich miast. W programie głównym wczytaj z tego pliku dane i stwórz na ich podstawie tablicę obiektów typu City.
Przykładowy plik cities.csv:
nazwa_miasta,ilosc_mieszkancow,dlugosc_geogr,szerokosc_geogr
Lublin,340000,51.253305,22.559572
Warszawa,1790658,52.250691,21.016754
Stalowa_Wola,60179,50.581978,22.054380

Rozwiązanie:
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>

struct City {
    std::string name;
    unsigned int population;
    double longitude;
    double latitude;
};

std::vector<City> read_cities(const std::string& filename) {
    std::vector<City> cities;

    std::ifstream file(filename);
    if (!file) {
        std::cout << "Error opening file: " << filename << std::endl;
        return cities;
    }

    std::string line;
    std::getline(file, line); // skip the header line

    while (std::getline(file, line)) {
        std::stringstream ss(line);
        std::string city, population, longitude, latitude;

        std::getline(ss, city, ',');
        std::getline(ss, population, ',');
        std::getline(ss, longitude, ',');
        std::getline(ss, latitude, ',');

        City newCity;
        newCity.name = city;
        newCity.population = std::stoi(population);
        newCity.longitude = std::stod(longitude);
        newCity.latitude = std::stod(latitude);

        cities.push_back(newCity);
    }

    file.close();
    return cities;
}

int main() {
    std::string filename = "cities.csv";
    std::vector<City> cities = read_cities(filename);

    for (const City& city : cities) {
        std::cout << "City: " << city.name << std::endl;
        std::cout << "Population: " << city.population << std::endl;
        std::cout << "Longitude: " << city.longitude << std::endl;
        std::cout << "Latitude: " << city.latitude << std::endl;
        std::cout << std::endl;
    }

    return 0;
}

Zadanie 22

Dla struktury City opracuj binarny format pliku do przechowywania danych o miastach. W języku C++ napisz funkcje do zapisu i odczytu tablicy z miastami z tak zdefiniowanego pliku binarnego.

Rozwiązanie:
#include <iostream>
#include <fstream>
#include <vector>

struct City {
    std::string name;
    unsigned int population;
    double longitude;
    double latitude;
};

void write_cities(const std::vector<City>& cities, const std::string& filename) {
    std::ofstream file(filename, std::ios::binary);

    for (const City& city : cities) {
        size_t nameLength = city.name.length();
        file.write((const char*)&nameLength, sizeof(size_t));
        file.write(city.name.c_str(), nameLength);
        file.write((const char*)&city.population, sizeof(unsigned int));
        file.write((const char*)&city.longitude, sizeof(double));
        file.write((const char*)&city.latitude, sizeof(double));
    }

    file.close();
}

std::vector<City> read_cities(const std::string& filename) {
    std::vector<City> cities;

    std::ifstream file(filename, std::ios::binary);
    if (!file) {
        std::cerr << "Error opening file: " << filename << std::endl;
        return cities;
    }

    while (true) {
        size_t nameLength;
        file.read(reinterpret_cast<char*>(&nameLength), sizeof(size_t));

        if (file.eof()) {
            break;
        }

        City city;
        city.name.resize(nameLength);
        file.read((char*)city.name.data(), nameLength);
        file.read((char*)&city.population, sizeof(unsigned int));
        file.read((char*)&city.longitude, sizeof(double));
        file.read((char*)&city.latitude, sizeof(double));

        cities.push_back(city);
    }

    file.close();
    return cities;
}

int main() {
    std::vector<City> cities = {
        {"Lublin", 340000, 51.253305, 22.559572},
        {"Warszawa", 1790658, 52.250691, 21.016754},
        {"Stalowa_Wola", 60179, 50.581978, 22.054380}
    };

    std::string filename = "cities.bin";
    write_cities(cities, filename);

    std::vector<City>  input_cities = read_cities(filename);
    for (const City& city : input_cities) {
        std::cout << "City: " << city.name << std::endl;
        std::cout << "Population: " << city.population << std::endl;
        std::cout << "Longitude: " << city.longitude << std::endl;
        std::cout << "Latitude: " << city.latitude << std::endl;
        std::cout << std::endl;
    }

    return 0;
}

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *