mgr inż. Wacław Libront * Bobowa 2019
ZSO Bobowa, ul. Długoszowskich 1, 38-350 Bobowa, tel: 0183514009, fax: 0183530221, email: sekretariat@zsobobowa.eu, www: zsobobowa.eu
Zmienne znakowe – char
Przykłady w ramce opisują deklarowanie i inicjowanie zmiennej znakowej, operacje dodawania liczby do zmiennej znakowej, różnicę pomiędzy znakiem ‘0’ liczbą 0 oraz konwersję znaku na liczbę i liczby na znak, kody ASCII i tzw.
rzutowanie.
char znak; znak='A'; cout << znak; //A //konwersja – znak-liczba cout << znak << int(znak) << endl;//A 65 cout << znak << (int) znak << endl;//A 65 cout << znak << char(65) << endl;//A A //znaki jak liczby!!! cout << znak+0 << znak+1 << endl;//65 66 cout << '1'+'1' << 1+1 << endl; //98 2 |
Konwersja
W języku C++ instrukcja cout << ‘1’+’1’ wyświetli na ekranie liczbę 98 (bo kod ASCII dla znaku ‘1’ to 49). Wszystkie normalne języki programowania
"widząc" znak traktują go jak znak i można go co najwyżej skleić z innym znakiem. Sam sposób zapisu konwersji znak-liczba też można w C++ "udziwnić". Typowy sposób to np.
char(65). W C++ można dodatkowo (char)65, albo też (char)(65).
Zadanie – Tablica ASCII
Wyświetl na ekranie tablicę kodów ASCII. Uporządkuj ją w 8 kolumnach: znak-kod.
for (int i=33; i<256; i++){ cout.width(4); cout << (char) (i); cout.width(4); cout << i; if (i % 8 == 0) cout << endl; } |
Znaki w kodzie ASCII, które są widoczne na ekranie konsoli mają numery w zakresie 32-255. Na przykład 32 to spacja, '1' znak jedynki to 48, 'A' duże A to 65, 'a' małe a 97. Spośród znaków niedrukowalnych: 13 - ENTER, 9-BACKSPACE, 27-ESC. Ta znajomość okazuje się bardzo przydatna na przykład podczas tworzenia konsolowych gier.
Zmienne napisowe STRING
W standardowym C nie było mowy o typowych zmiennych tekstowych (string), jak
w innych językach programowania. Twórca – dążąc do minimalizacji wymyślił
łańcuchy znakowe (tablice), co w praktyce spowodowało mnóstwo problemów. Co więcej, niektóre wersje kompilatorów (np. Dev) potrafią operować na napisach bez
dodatkowych bibliotek, ale już MSVC++ musi mieć zadeklarowaną bibliotekę <string>
Uwaga. Pierwsza litera w napisie ma w tablicy znaków numer zero!
LENGTH - długość napisu
APPEND - doklejanie na końcu
INSERT - wstawianie
ERASE - usuwanie
AT lub [] – znak – znak
FIND - wyszukiwanie
SUBSTR - wycinanie
string nap="Wacław Libront"; cout << nap << endl; //długość napisu - LENGTH int dlug=nap.length(); //doklejanie za pomocą operatora + nap="mgr inż "+nap; cout << nap << endl; //doklejanie na końcu - APPEND nap=nap.append(" Bobowa"); cout << nap << endl; //wstawianie do napis - INSERT nap=nap.insert(15,"Stanisław "); cout << nap << endl; //usuwanie fragmentu napisu - ERASE nap=nap.erase(15,10); cout << nap << endl; //wybieranie znaku z napisu - AT char znak=nap.at(6); cout << znak << endl; //wybieranie znaku z napisu - tablica znak=nap[6]; cout << znak << endl; //wyszukiwanie znaków i napisów - FIND int spacja=nap.find(" "); cout << spacja << endl; //wyszukanie kolejnej spacji w tekście cout << nap.find(" ",spacja+1) << endl; //wycinanie fragmentów - SUBSTR string frag=nap.substr(spacja+1,3); cout << frag << endl; |
Ćwiczenia z napisami
Wpisz z klawiatury (lub zadeklaruj w zmiennej) napis, w którym będzie twoje Nazwisko i Imię.
wyodrębnij nazwisko i imię, do dwóch zmiennych
stwórz anagramy nazwiska i imienia
zamieniamy miejscami nazwisko z imieniem
zamień miejscami pierwsze litery - zlepek Nmię Iazwisko
wyświetl inicjały NI
string NI="Libront Wacław"; //długość i spacja int DLUG=NI.length(); int SPAC=NI.find(" "); //wyodrębnienie wyrazów string IMIE=NI.substr(0,SPAC); string NAZW=NI.substr(SPAC+1,DLUG-SPAC); cout << IMIE << " " << NAZW << endl; //pierwsze litery - inicjały char INII=IMIE[0]; char ININ=NAZW[0]; cout << INII << " " << ININ << endl; // zamieniamy literki cout << IMIE[0] << NAZW.substr(1,NAZW.length()); cout << " "; cout << NAZW[0] << IMIE.substr(1,IMIE.length()); cout << endl; //zamieniamy na małe <> duże for (int i=0; i < DLUG; i++) if (NI[i] >= 65 && NI[i] <= 90) cout << char(NI[i]+32); else if (NI[i] >= 97 && NI[i] <= 122) cout << char(NI[i]-32); else cout << NI[i]; cout << endl; //anagramy for (int i=NAZW.length()-1; i >=0 ; i--) cout << NAZW[i]; for (int i=IMIE.length()-1; i >= 0; i--) cout << IMIE[i]; cout << endl; |
Konwersja tekst-liczba
Dowolny napis przedstawiający liczbę przekonwertujemy na liczbę za pomocą funkcji
atoi (na całkowitą) lub atof (rzeczywistą).
cout << atoi("213")+1 << endl; cout << atof("213")+1.1 << endl; |
Konwersja liczba tekst
Konwersja w drugą stronę jest w języku C problematyczna i twórcy nie wykazali się pomysłowością. Istnieje mnóstwo sposobów wymyślonych przez niezależnych twórców, ale żaden z nich nie jest doskonały i nie ma jednej wygodnej funkcji.
Zamiana liczby całkowitej na tekst za pomocą funkcji itoa jest jak najbardziej możliwa, ale … twórca wymyślił, że będzie też możliwość zamiany na dowolny system pozycyjny – w naszym przykładzie podstawą systemu jest 10.
int liczba=123; char L[20]; itoa(liczba,L,10); cout << strlen(L) << endl; |
Nie ma niestety funkcji ftoa i jeśli chcemy zamienić liczbę rzeczywistą na tekst możemy posłużyć się biblioteką <sstream>. Do zmiennej strumieniowej wlewamy liczbę, a następnie zamieniamy ją na stringa.
#include <sstream> ... stringstream zmienna; zmienna << 123.31; string tekst=zmienna.str(); cout << tekst.length() << endl; |
Zamiana DWÓJKOWE-DZIESIĘTNE
Wpisz liczbę dwójkową z klawiatury (w postaci tekstu) i zamień na jej dziesiętny odpowiednik. Napisz odpowiednią funkcję zamieniającą dwójowy zapis na liczbę dziesiętną.
Jak zamienić liczbę dwójkową na dziesiątkową? Z matematycznego punktu wiedzenia nie jest to trudne – wystarczy zsumować tzw. wagi cyfr w liczbie dwójkowej. Co to oznacza? Każda cyfra w liczbie jest numerowana – najmniej znacząca (pierwsza z prawej) jest na pozycji zerowej, kolejna na pozycji pierwszej, itd. Wystarczy liczbę 2 (podstawę systemu dwójkowego) podnieść do potęgi, która jest cyfrą na tej pozycji . Te wszystkie potęgi (tzw. wagi bitów) należy zsumować – i to jest będzie liczba w systemie dziesiątkowym.
Funkcja DW_DZ() wykonuje te wszystkie operacje, w jak najbardziej „edukacyjny” sposób (istnieje wiele innych algorytmów). Podczas wyodrębniania poszczególnych bitów z napisu będziemy odwoływać się do tablicy napisu, pamiętając o tym, że bity musimy pobierać od tyłu!
int DW_DZ(string dw){ const int POD=2; char bit; int waga; int poz=0; int dz=0; int dlug=dw.length(); do { bit=dw[dlug-poz-1]; waga=pow(POD,poz); if (bit != '0') dz=dz+waga; poz++; } while (poz < dlug); return dz; } |
Zamiana DZIESIĘTNE- DWÓJKOWE
Wpisz z klawiatury liczbę dziesiętną. Na ekranie wypisz jej dwójkowy odpowiednik. Napisz funkcję zamieniającą liczby dziesiętne na dwójkowe, której wynikiem jest tekst zero-jedynkowy.
Dzielimy całkowicie liczbę przez dwa. Reszty z dzielenia (zera lub jedynki) sklejamy w liczbę dwójkową – kolejne bity doklejamy zawsze na początek! Wynik z dzielenia całkowitego przez dwa staje się na końcu naszą liczbą podstawową i całą operację powtarzamy, dopóki można wykonywać dzielenie.
UWAGA. Należy rozróżnić zmienną napisową dw, która na początki jest tzw. „łańcuchem pustym ”” od znaków ‘0’ i ‘1’w apostrofach, które są doklejane do łańcucha dw za pomocą zwykłej operacji dodawania.
string DZ_DW_(int dz){ string dw=""; int bit; do { bit=dz % 2; if (bit == 1) dw='1'+dw; else dw='0'+dw; dz=dz/2; } while (dz > 0); return dw; } |
Wczytywanie tekstów z klawiatury
- getline
Do tej pory wczytywaliśmy z klawiatury (za pomocą polecenia cin) do zmiennych pojedyncze liczby lub teksty. Kłopoty pojawiają się ponieważ cin nie reaguje na spacje i entery. Wystarczy uruchomić sprawdzić ciąg instrukcji z ramki i wpisać z klawiatury wielowyrazowy napis, np. „Nazwisko Imię” rozdzielone spacją. Po wciśnięciu klawisza ENTER na ekranie wyświetlone zostanie tylko „Nazwisko”, gdyż cin wczytuje znaki do napotkania spacji! Jak sobie można z tym poradzić? Jednym z rozwiązań jest zastosowanie instrukcji getline(cin, zmienna).
UWAGA. Jeśli będziemy mieszać w programie cin i getline pojawią się kolejne problemy związane z tzw. buforem klawiatury :-)
string N; cin >> N; cout << N; |
Zadanie – Ulica i numer domu
Wczytuj w pętli do tablicy ulicę wraz z numerem domu, aż do naciśnięcia klawisza ENTER – wczytania pustego tekstu.
Zadanie zrealizowano za pomocą instrukcji getline i pętli while, która kończy swoje działanie, gdy w zmiennej, do której wczytujemy napisy z klawiatury będzie tekst pusty. Na końcu wypisujemy wszystkie wczytane napisu na ekranie.
int ile=0; string u; string ulice[100]; do{ cout << "Ulica: "; getline(cin,u); if (u != "") ulice[ile]=u; ile++; } while(u != ""); for (int i=0; i < ile; i++) cout << ulice[i] << endl; |
Zadanie - Zamiana liczb
Korzystając z powyższych funkcji można napisać identycznie działające, które zamieniają dowolny system pozycyjny na dowolny inny w zakresie 2..16. Problemy mogą pojawiać się podczas podnoszenia do potęgi za pomocą funkcji POW, która ma problemy z liczbami całkowitymi (100^2=99)
A skoro mamy do dyspozycji obie funkcje, to wykorzystamy je do napisania funkcji, która zamienia liczbę z dowolnego systemy na dowolny inny w zakresie 2-16.
//zamiana dziesiątkowego na dowolny 2-16 string DZ_XX(int dz, int podstawa){ string X="0123456789ABCDEF"; string xx=""; int bit; do { bit=dz % podstawa; xx=X[bit]+xx; dz=dz/podstawa; } while (dz>0); return xx; } //zamiana dowolnego 2-16 na dziesiątkowy int XX_DZ(string xx, int podstawa){ string X="0123456789ABCDEF"; char bit; int bitC;//bit zamieniony na cyfrę int poz=0; int dz=0; int dlug=xx.length(); do { bit=xx[dlug-poz-1]; bitC=X.find(bit); double w=pow(podstawa,poz); dz=dz+int(w)*bitC; poz++; } while (poz < dlug); return dz; } //zamiana z dowolnego na dowolny string XX_XX(string xx, int p1, int p2){ return DZ_XX(XX_DZ(xx,p1),p2); } ... //zamiana 123 w systemie siódemkowym na system trójkowy cout << XX_XX("123",7,3) << endl; |
1) Napisz funkcję ANAGRAM, która zamieni podany jako parametr tekst na jego odwrotność
2) Napisz funkcję, która utworzy tekst złożony z losowego zlepku liter podanych w parametrze
3) Napisz funkcję, która policzy ilość liter w tekście. Jako parametr podajemy tekst i literę
4) Napisz funkcję, która policzy wszystkie litery w tekście – bez spacji
5) Napisz funkcję PALINDROM, która sprawdzi czy wpisany wyraz jest palindromem (czytany od tyłu jest taki sam).
a) Zmodyfikuj funkcję aby sprawdzała całe zdania - usuń najpierw spacje z tekstu!
6) Mini szyfrowanie. Napisz funkcję, która zwróci zaszyfrowany tekst. Zasada szyfrowania: kod ASCII każdego znaku zwiększamy o konkretną liczbę
7) Mini szyfrowanie. Napisz funkcję, która zwróci zaszyfrowany tekst. Zasada szyfrowania: przestawiamy znaki w tekście 1-2 3-4 5-6 itd.
8) Napisz funkcję, która zliczy samogłoski (spółgłoski) w tekście
9) Napisz funkcję, która zliczy wyrazy w tekście. Zliczamy spacje +1
10) Dwie duże liczby znajdują się w łańcuchach tekstowych. Wykonaj ich dodawanie.
Przykładowe rozwiązania zadań
Jedynie ciężka umysłowa praca i zrozumienie działania
programu może nauczyć programowania!
//zadanie 1 string ANAGRAM(string n){ string m=""; int d=n.length(); for (int i=0; i < d; i++) m=n[i]+m; return m; } string napis; cout << "napis: "; cin >> napis; cout << ANAGRAM(napis) << endl; |
//zadanie 2 //lsosowy zlepek liter z napisu startowego //ale wybieramy tylko po jednej // i trzeba pamietac która była albo wycinać string LOSOWO(string n){ string m=""; int d=n.length(); for (int i=d; i > 0; i--){ int nr=rand() % i; string litera=n.substr(nr,1); n=n.erase(nr,1); m=m+litera; } return m; } string napis; cout << "napis: "; cin >> napis; cout << LOSOWO(napis) << endl; cout << LOSOWO(napis) << endl; cout << LOSOWO(napis) << endl; |
//zadanie 3 int ILE_LITER(string n, char l){ int d=n.length(); int ile=0; for (int i=0; i < d; i++) if (n[i] == l) ile++; return ile; } string nap; char znak; cout << "napis i litera: "; cin >> nap; cin >> znak; cout << ILE_LITER(nap,znak) << endl; |
//zadanie 4 //ile liter bez spacji int ILE_ZNAKOW(string n){ int d=n.length(); int ile=0; for (int i=0; i < d; i++) if (n[i] != ' ') ile++; return ile; } //wczytujemy getline string zdanie; cout << "zdanie: "; getline(cin,zdanie); cout << ILE_ZNAKOW(zdanie) << endl; |
//zadanie 5 //palindrom - wspak to samo //wystarczy ze sprawdziwmy do połowy //jesli palindrom to na końcu true //jesli znaki różne to od razu kończy pętle z false bool PALINDROM(string n){ int d=n.length(); for (int i=0; i <= d/2; i++){ char z1=n[i]; char z2=n[d-i-1]; if (z1 != z2) return false; } return true; } string pal; cout << "palindrom: "; cin >> pal; cout << PALINDROM(pal) << endl; //wersja palindroma dla całych zdań //a to kanapa pana kota //kobyła ma mały bok //palindromowe zdania ze spacjami //najpierw usuwamy spacje ze zdania bool PALINDROM1(string n){ int d=n.length(); string m=""; for (int i=0; i < d; i++) if (n[i] != ' ') m=m+n[i]; d=m.length(); for (int i=0; i <= d/2; i++){ char z1=m[i]; char z2=m[d-i-1]; if (z1 != z2) return false; } return true; } string pal; cout << "palindrom: "; getline(cin,pal); cout << PALINDROM1(pal) << endl; |
//zadanie 6 string SZYFR1(string n, int kod){ int d=n.length(); for (int i=0; i < d; i++){ // znak ASCII zamieniamy na liczbę //do liczby dodajemy kod //nową liczbę zamieniamy na znak ASCII //podmieniamy znak w napisie n[i] = char(int(n[i]) + kod); } return n; } string szy; int kod; cout << "napis: "; getline(cin,szy); cout << "kod: "; cin >> kod; szy=SZYFR1(szy,kod); cout << szy << endl; //sprawdzamy w drugą stronę szy=SZYFR1(szy,-kod); cout << szy << endl; |
//zadanie 7 string SZYFR2(string n){ int d=n.length(); //jesli nieparzysta to zmniejszamy o jeden //zeby nie brało znaku spoza napisu if (d%2 == 1) d=d-1; for (int i=0; i < d; i=i+2){ //podmieniamy znaki w napisie char z1=n[i]; char z2=n[i+1]; n[i]=z2; n[i+1]=z1; } return n; } //zadanie 7 szyfr string sz; cout << "napis: "; getline(cin,sz); sz=SZYFR2(sz); cout << sz << endl; //sprawdzamy w drugą stronę sz=SZYFR2(sz); cout << sz << endl; |
//zadanie 8 int SAMOGLOSKI(string n){ string SA="aeiouyAEIOUY"; int ile=0; int d=n.length(); for (int i=0; i < d; i++) //szukamy znaku w SA if (SA.find(n[i]) < d) ile++; return ile; } string nap; cout << "napis: "; getline(cin,nap); cout << SAMOGLOSKI(nap) << endl; |
//zadanie 9 int WYRAZY(string n){ int ile=0; int d=n.length(); for (int i=0; i < d; i++) if (n[i] == ' ') ile++; //zliczamy spacje, ale wyrazów jeden wiecej return ile+1; } string nap; cout << "napis: "; getline(cin,nap); cout << WYRAZY(nap) << endl; |
//zadanie 10 string DODAWANIE(string liczba1, string liczba2){ string wynik=""; //dlugosci liczb int dl1=liczba1.length(); int dl2=liczba2.length(); //wyznaczenie dłuższej liczby //i uzupełnienie początku krótszej zerami int dl; string sroz=""; int roz=abs(dl2-dl1); for (int i=1; i <= roz; i++) sroz=sroz+"0"; if (dl1>dl2){ dl=dl1; liczba2=sroz+liczba2; } else{ dl=dl2; liczba1=sroz+liczba1; } //dodawanie znak-cyfra-znak int c1,c2,c3; int przenies=0; for (int i=dl-1; i >= 0; i--){ //zamiana na liczbe!!! c1=int(liczba1[i])-48; c2=int(liczba2[i])-48; c3=c1+c2+przenies; przenies=0; if (c3 >= 10){ c3=c3-10; przenies=1; } //zamiana na tekst!!! wynik=char(c3+48)+wynik; } if (przenies==1) wynik='1'+wynik; return wynik; } //wczytanie tekstów liczb string liczba1,liczba2,wynik; cout<<"Podaj pierwszą liczbę: "; cin>>liczba1; cout<<"Podaj drugą liczbę: "; cin>>liczba2; wynik=DODAWANIE(liczba1,liczba2); //wyniki na ekran cout.width(20); cout << liczba1 << endl; cout.width(20); cout << liczba2 << endl; cout.width(20); cout << wynik << endl; |
//SZABLON #include <stdlib.h> //system #include <iostream> //cout #include <iomanip> //fixed #include <cmath> //pow using namespace std; int main(){ setlocale(LC_ALL, ""); system("pause"); } |