Kurs podstawy programowania UMCS, zdjęcie laptopa.

Podstawy programowania – Laboratorium 14

Wejściówka

Zaprojektuj strukturę Point, która posiada dwa pola typu zmiennoprzecinkowego x, y, następnie napisz funkcję, która przyjmuje dwa argumenty typu Point. Metoda powinna zwrócić odległość między punktami przekazanymi w argumencie.

Rozwiązanie:
#include <cmath>

struct Point {
    float x, y;
};

float distance(const Point &p1, const Point &p2) {return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));}

Zadanie 1 – 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 <cstring>

void print_all_file_words(const char *filename) {
    char temp_str[100] = {};
    char** temp_arr = {};
    int count = 0;
    FILE *in = nullptr;
    if((in = fopen(filename, "rt")) == nullptr) {
        printf("Błąd!\n");
    } else {
        while(fscanf(in, "%99s", temp_str) != EOF) count++;
        temp_arr = new char*[count];
        for(int i = 0; i < count; ++i) temp_arr[i] = new char[100];
        int i = 0;
        fseek(in, 0, SEEK_SET); // rewind(in);
        while(fscanf(in, "%99s", temp_str) != EOF) strcpy(temp_arr[i++], temp_str);
        fclose(in);
    }

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


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*.

W ramach tego kursu poznamy jedynie ten drugi sposób. 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 2

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 = nullptr, i = 0, count = 0, tmp, min, max;
    FILE *in; 
    if((in = fopen(filename, "rt")) == NULL) {
        printf("Bład!");
    } else {
        while(fscanf(in, "%d ", &tmp) != EOF) count++;
        arr = new int[count];
        fseek(in, 0, SEEK_SET);
        while(fscanf(in, "%d ", &tmp) != EOF) arr[i++] = 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", arr[0], arr[count - 1]);
    min = find_min(arr, count);
    max = find_max(arr, count);
    printf("%d %d\n", min, max);
    delete[] arr;
}

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

Zadanie 3

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 4

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 5

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 6

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.

Rozwiązanie:
#include <cstdio>

void incrementNumbersInFile(const char *filename) {
    int *numbers = nullptr, count = 0, tmp;
    FILE *in = nullptr, *out = nullptr;
    if((in = std::fopen(filename, "r")) == nullptr) {
        std::perror("Bład!");
        return;
    } else {
        while(std::fscanf(in, "%d", &tmp) == 1) count++;
        std::rewind(in);
        numbers = new int[count];
        int i = 0;
        while(std::fscanf(in, "%d", &tmp) == 1) numbers[i++] = tmp + 1;
        std::fclose(in);
    }

    if((out = std::fopen(filename, "w")) == nullptr) {
        std::perror("Bład!");
        delete[] numbers;
        return;
    } else {
        for(int i = 0; i < count; ++i) std::fprintf(out, "%d ", numbers[i]);
        std::fclose(out);
    }

    delete[] numbers;
}

int main() {
    const char *filePath = "liczby.txt";
    incrementNumbersInFile(filePath);
    return 0;
}

Zadanie 7

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 <cstdio>
#include <cstdlib>
#include <ctime>

int compare(const void *a, const void *b) {
    return *(int*)a - *(int*)b;
}

void insertRandomNumbers(const char *filename) {
    int *numbers = nullptr, count = 0, tmp, min, max;
    FILE *file = nullptr;

    if((file = std::fopen(filename, "r+")) == nullptr) {
        std::perror("Bład!");
        return;
    } else {
        while(std::fscanf(file, "%d", &tmp) == 1) ++count;
        std::rewind(file);
        numbers = new int[count + 5];
        int i = 0;
        while(std::fscanf(file, "%d", &tmp) == 1) numbers[i++] = tmp;
        min = numbers[0];
        max = numbers[count - 1];

        for(int i = 0; i < 5; ++i) numbers[count++] = min + std::rand() % (max - min + 1);
        std::qsort(numbers, count, sizeof(numbers[0]), compare);

        std::fseek(file, 0, SEEK_SET);
        for(int i = 0; i < count; ++i) std::fprintf(file, "%d ", numbers[i]);
        std::fclose(file);
        delete[] numbers;
    }
}

int main() {
    std::srand(std::time(0));
    const char *filePath = "liczby.txt";
    insertRandomNumbers(filePath);
    return 0;
}

Zadanie 8

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 <cstdio>
#include <cstring>

void replaceAsterisks(const char* filename, const char* word) {
    FILE *file = NULL;
    char buffer[256];

    if ((file = std::fopen(filename, "r+")) == NULL) {
        perror("Blad otwierania pliku!");
        return;
    } else {
        while (fgets(buffer, sizeof(buffer), file)) {
            char* pos = strstr(buffer, "*****");
            if (pos != NULL) {
                long offset = ftell(file) - strlen(buffer) + (pos - buffer);
                fseek(file, offset, SEEK_SET);
                fprintf(file, "%s", word);
                fseek(file, offset + strlen(word), SEEK_SET);
            }
        }
        std::fclose(file);
    }       
}

int main() {
    const char* filePath = "tekst.txt";
    char word[6] = "rybke";
    replaceAsterisks(filePath, word);
    return 0;
}
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.

Zadanie 9

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 <cstdio>
#include <cstring>
#include <cstdlib>

void replaceAsterisks(const char* filename, const char* word) {
    FILE *file = NULL;
    char *buffer = NULL, *newBuffer = NULL, *pos = NULL;
    const char* pattern = "*****";
    size_t fileSize, newSize, patternLength = strlen(pattern), wordLength = strlen(word);

    if ((file = std::fopen(filename, "r")) == NULL) {
        perror("Błąd otwierania pliku!");
        return;
    }

    std::fseek(file, 0, SEEK_END);
    fileSize = std::ftell(file);
    std::rewind(file);

    buffer = (char*)malloc(fileSize + 1);
    std::fread(buffer, 1, fileSize, file);
    buffer[fileSize] = '\0';
    std::fclose(file);


    int asterisksCount = 0;
    pos = buffer;
    while ((pos = strstr(pos, pattern)) != NULL) {
        asterisksCount++;
        pos += patternLength;
    }
    newSize = fileSize + asterisksCount * (wordLength - patternLength);
    newBuffer = (char*)malloc(newSize + 1);
    
    
    char* src = buffer;
    char* dst = newBuffer;
    while ((pos = strstr(src, pattern)) != NULL) {
        size_t copyLength = pos - src;
        strncpy(dst, src, copyLength);
        dst += copyLength;
        strcpy(dst, word);
        dst += wordLength;
        src = pos + patternLength;
    }
    strcpy(dst, src);
    
    if ((file = std::fopen(filename, "w")) == NULL) {
        perror("Błąd!");
        free(buffer);
        free(newBuffer);
        return;
    }
    std::fwrite(newBuffer, 1, strlen(newBuffer), file);
    std::fclose(file);
    free(buffer);
    free(newBuffer);
}

int main() {
    const char* filePath = "tekst.txt";
    char word[] = "kotka i rybke";
    replaceAsterisks(filePath, word);
    return 0;
}

Zadanie 10

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 <cstdio>

void readCatData(const char* filename) {
    int age = 0, numCats = 0;
    FILE *file = NULL;
    if ((file = std::fopen(filename, "r")) == NULL) {
        perror("Nie można otworzyć pliku!");
        return;
    }

    fscanf(file, "Ala ma %d kotów w następującym wieku:\n", &numCats);
    printf("Ala ma %d kotów w następującym wieku:\n", numCats);

    for (int i = 0; i < numCats; i++) {
        fread(&age, sizeof(int), 1, file);
        printf("Kot %d: %d lat\n", i + 1, age);
    }
    fclose(file);
}

int main() {
    const char* filename = "cats_data.bin";
    readCatData(filename);
    return 0;
}

Zadanie 11

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>
#include <iomanip>

int main() {
    char name[20] = {};
    int age;
    int height;
    double weight;
    std::cin >> name >> age >> height >> weight;

    std::cout << std::right << std::setw(20) << "Imię: " << name << std::endl;
    std::cout << "Wiek (szesnastkowo): " << std::hex << age << std::endl;
    std::cout << "Wzrost (dziesiętnie): " << std::dec << height << std::endl;
    std::cout << std::fixed << std::setprecision(3) << "Waga: " << weight << std::endl;

    return 0;
}

Zadanie 12

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 13 *

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 14

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 <cstdio>

void write_array(const char* filename, float* array, size_t size) {
    FILE *file = NULL;
    if ((file = std::fopen(filename, "wb")) == NULL) {
        perror("Nie można otworzyć pliku do zapisu!");
        return;
    }
    fwrite(array, sizeof(float), size, file);
    fclose(file);
}

size_t read_array(const char* filename, float* array, size_t max_size) {
    FILE *file = NULL;
    if ((file = std::fopen(filename, "rb")) == NULL) {
        perror("Nie można otworzyć pliku do odczytu!");
        return 0;
    }
    size_t elementsRead = fread(array, sizeof(float), max_size, file);
    fclose(file);
    return elementsRead;
}

int main() {
    const char* filename = "data.bin";
    float array[] = {1.5, 2.5, 3.5, 4.5};
    size_t size = 4;

    write_array(filename, array, size);
    float readArray[4];
    size_t elements = read_array(filename, readArray, size);

    for (size_t i = 0; i < elements; i++) printf("%.1f ", readArray[i]);
    printf("\n");

    return 0;
}

Zadanie 15

W języku C++ zdefiniuj strukturę City opisującą miasto i zawierającą składowe: nazwa miasta (c-string, maksymalnie 30 znaków), 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 <cstdio>
#include <cstring>
#include <cstdlib>

struct City {
    char name[31];
    unsigned int population;
    double latitude;
    double longitude;
};

void loadCitiesFromCSV(const char* filename, City* cities, size_t& count) {
    FILE* file = fopen(filename, "r");
    if (file == nullptr) {
        perror("Nie można otworzyć pliku!");
        return;
    }

    char buffer[256];
    fgets(buffer, sizeof(buffer), file);

    count = 0;
    while (fgets(buffer, sizeof(buffer), file)) {
        City city;
        char* token = strtok(buffer, ",");
        strncpy(city.name, token, 30);
        token = strtok(nullptr, ",");
        city.population = atoi(token);
        token = strtok(nullptr, ",");
        city.latitude = atof(token);
        token = strtok(nullptr, ",");
        city.longitude = atof(token);
        cities[count++] = city;
    }
    fclose(file);
}

int main() {
    const char* filename = "cities.csv";
    City cities[100];
    size_t cityCount;
    loadCitiesFromCSV(filename, cities, cityCount);
    for (size_t i = 0; i < cityCount; i++)
        printf("Miasto: %s, Populacja: %u, Szerokość geogr.: %.6f, Długość geogr.: %.6f\n",
               cities[i].name, cities[i].population, cities[i].latitude, cities[i].longitude);
    return 0;
}

Zadanie 16

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 <cstdio>
#include <cstring>

struct City {
    char name[31];
    unsigned int population;
    double latitude;
    double longitude;
};

void writeCitiesToBinary(const char* filename, City* cities, size_t count) {
    FILE* file = fopen(filename, "wb");
    if (file == nullptr) {
        perror("Nie można otworzyć pliku do zapisu!");
        return;
    }
    fwrite(cities, sizeof(City), count, file);
    fclose(file);
}

size_t readCitiesFromBinary(const char* filename, City* cities, size_t max_count) {
    FILE* file = fopen(filename, "rb");
    if (file == nullptr) {
        perror("Nie można otworzyć pliku do odczytu!");
        return 0;
    }
    size_t elementsRead = fread(cities, sizeof(City), max_count, file);
    fclose(file);
    return elementsRead;
}

int main() {
    size_t cityCount = 3;
    const char* filename = "cities.bin";
    City cities[cityCount] = {
        {"Lublin", 340000, 51.253305, 22.559572},
        {"Warszawa", 1790658, 52.250691, 21.016754},
        {"Stalowa_Wola", 60179, 50.581978, 22.054380}
    };

    writeCitiesToBinary(filename, cities, cityCount);
    City loadedCities[cityCount];
    size_t loadedCount = readCitiesFromBinary(filename, loadedCities, cityCount);

    for (size_t i = 0; i < loadedCount; i++)
        printf("Miasto: %s, Populacja: %u, Szerokość geogr.: %.6f, Długość geogr.: %.6f\n",
               loadedCities[i].name, loadedCities[i].population,
               loadedCities[i].latitude, loadedCities[i].longitude);
    return 0;
}

Dodaj komentarz

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