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, offset = 0; 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 + offset, std::ios::beg); f << str; } offset += line.length(); } 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; }