Serwis Edukacyjny w I-LO w Tarnowie ![]() Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2023 mgr Jerzy Wałaszek |
Tekst jest ciągiem znaków ASCII, zatem do jego przechowania potrzebna jest tablica znakowa, której poszczególne komórki przechowują kolejne znaki tekstu. Tablicę znakową tworzymy wg tych samych zasad, co tablice numeryczne:
char nazwa_tablicy[liczba_znaków];
lub
unsigned char nazwa_tablicy[liczba_znaków];
signed char nazwa_tablicy[liczba_znaków];
Tutaj pojawia się pewien problem. Teksty mogą mieć różną długość, czyli liczbę znaków. Aby efektywnie przetwarzać teksty, komputer musi wiedzieć, ile znaków dany tekst zawiera. W języku C++ przyjęto zasadę, iż tekst kończy się znakiem NUL, czyli kodem 0. Sam kod 0 nie należy do tekstu. Poniższy program odczytuje tekst, umieszcza go w tablicy znakowej, po czym wyświetla w oknie konsoli:
// Tekst //------ #include <iostream> using namespace std; int main() { char imie[100]; setlocale(LC_ALL,""); cout << "Jak brzmi twoje imię? : "; cin >> imie; cout << endl << "Witaj " << imie << "!" << endl << endl; return 0; } |
Jak brzmi twoje imię? : Janusz Witaj Janusz! |
Przeanalizujmy działanie tego programu.
char imie[100]; |
Tworzymy tablicę znakową o pojemności 100 znaków. Tutaj chodzi o zapas, aby nie okazało się, iż wprowadzane imię nie zmieści się w tablicy. | |
setlocale(LC_ALL,""); |
Ustawiamy w konsoli wyświetlanie polskich znaków Windows 1250. | |
cin >> imie; |
Odczytujemy imię użytkownika do tablicy znakowej. Strumień wejścia automatycznie dodaje znak NUL za ostatnim znakiem tekstu. Zwróć uwagę, iż nazwa tablicy jest jednocześnie wskaźnikiem pierwszego jej znaku. | |
cout << endl << "Witaj " << imie << "!" |
Wyświetlamy odczytane znaki. Zwróć uwagę, iż tutaj również nazwa tablicy jest potraktowana jako wskaźnik jej pierwszego znaku. |
Wszystko jest wspaniale, dopóki wprowadzone imię nie zawiera polskich znaków:
Jak brzmi twoje imię? : Świętosława Witaj -wictos?awa! |
Co się stało? Otóż okazuje się, iż funkcja setlocale( ) zmieniła jedynie sposób wyświetlania polskich znaków. Odczyt dalej jest w LATIN 2! To zalety wspaniałego systemu Windows. W Linxie wszystko jest w porządku, a funkcja setlocale( ) wcale nie jest potrzebna. Rozwiązaniem problemu jest stworzenie prostej funkcji, która przetworzy odczytany tekst na kodowanie Win 1250:
// Tekst //------ #include <iostream> using namespace std; void PL(char * t) { while(*t) { switch((unsigned char)*t) { case 164 : *t = 165; break; case 143 : *t = 198; break; case 168 : *t = 202; break; case 157 : *t = 163; break; case 227 : *t = 209; break; case 224 : *t = 211; break; case 151 : *t = 140; break; case 141 : *t = 143; break; case 189 : *t = 175; break; case 165 : *t = 185; break; case 134 : *t = 230; break; case 169 : *t = 234; break; case 136 : *t = 179; break; case 228 : *t = 241; break; case 162 : *t = 243; break; case 152 : *t = 156; break; case 171 : *t = 159; break; case 190 : *t = 191; break; default : break; } t++; } } //--------------------------------------- int main() { char imie[100]; setlocale(LC_ALL,""); cout << "Jak brzmi twoje imię? : "; cin >> imie; PL(imie); cout << endl << "Witaj " << imie << "!" << endl << endl; return 0; } |
Jak brzmi twoje imię? :
książę_zażółć_gęślą_jaźń Witaj książę_zażółć_gęślą_jaźń! |
Jak działa nasza funkcja:
void PL(char * t) |
Funkcja w parametrze t otrzymuje adres tablicy znakowej, której znaki są w kodzie LATIN2. | |
while(*t) |
Tworzymy pętlę, w której wskaźnik t przechodzi przez kolejne znaki tekstu, aż do napotkania kodu NUL. Wtedy wyrażenie *t ma wartość 0, czyli logicznie jest fałszywe i pętla while zakończy działanie. | |
switch((unsigned char)*t) |
W pętli sprawdzamy kody napotkanych znaków traktowane jako liczby bez znaku (inaczej kody byłyby ujemne). | |
kod_LATIN2: *t = kod_WIN_1250; break; |
Jeśli napotkany znak ma kod LATIN2 polskiego znaku, to zmieniamy go w tablicy na kod WIN 1250. Rozkaz break; kończy działanie instrukcji switch( ). Znaki nie będące kodami LATIN2 polskich znaków nie są zmieniane (klauzula default:). Znaki są traktowane w języku C++ jak liczby o wartości kodów tych znaków. | |
t++; |
Po wyjściu z instrukcji switch( ) wskaźnik t jest zwiększany i wskazuje kolejny znak w tablicy. Wykonywane jest to na końcu każdego obiegu pętli while. |
Zachowaj sobie tę funkcję. Jeszcze będzie przydatna.
Strumień cin pozwala odczytywać pojedyncze wyrazy, ponieważ spacja jest separatorem. Zatem jeśli wprowadzimy imię dwuwyrazowe, np. Don Kijote, to ze strumienia zostanie pobrany tylko pierwszy wyraz:
Jak brzmi twoje imię? : Don Kijote Witaj Don! |
Drugi wyraz pozostanie w strumieniu i wymaga ponownego użycia cin, aby go odczytać. Jeśli zatem chcemy odczytać cały wiersz, to musimy użyć funkcji składowej strumienia cin o nazwie getline( ). Funkcja ma dwa parametry: tablicę znakową do której zostanie wprowadzony tekst oraz maksymalną liczbę znaków w tablicy. Jeśli wprowadzony tekst jest dłuższy, to zostanie obcięty tak, aby zmieścił się w tablicy. Pozostałe znaki są usuwane ze strumienia. Ponieważ na końcu tekstu jest automatycznie dopisywany znak NUL, to do tablicy trafi tekst o liczbie znaków o jeden mniejszej od maksymalnej. Chodzi o to, aby obszar tablicy w pamięci nie został przekroczony, gdyż to mogłoby spowodować błędne działanie programu. Składnia funkcji jest następująca:
cin.getline(tablica_znakowa,maksymalna_liczba_znaków)
Sprawdź poniższy program:
// Tekst //------ #include <iostream> using namespace std; void PL(char * t) { while(*t) { switch((unsigned char)*t) { case 164 : *t = 165; break; case 143 : *t = 198; break; case 168 : *t = 202; break; case 157 : *t = 163; break; case 227 : *t = 209; break; case 224 : *t = 211; break; case 151 : *t = 140; break; case 141 : *t = 143; break; case 189 : *t = 175; break; case 165 : *t = 185; break; case 134 : *t = 230; break; case 169 : *t = 234; break; case 136 : *t = 179; break; case 228 : *t = 241; break; case 162 : *t = 243; break; case 152 : *t = 156; break; case 171 : *t = 159; break; case 190 : *t = 191; break; default : break; } t++; } } int main() { char imie[100]; setlocale(LC_ALL,""); cout << "Jak brzmi twoje imię? : "; cin.getline(imie,100); PL(imie); cout << endl << "Witaj " << imie << "!" << endl << endl; return 0; } |
Jak brzmi twoje imię? : Król Maciuś Pierwszy
Długouchy lecz Wąsaty Witaj Król Maciuś Pierwszy Długouchy lecz Wąsaty! |
char tablica_znakowa[ ] = "...dowolny tekst...";
Zwróć uwagę, iż w tym przypadku nie musimy podawać rozmiaru tablicy, kompilator wyliczy rozmiar z długości tekstu, chociaż możesz to zrobić np. w sytuacji, gdy chcesz mieć tablicę o większej liczbie znaków, niż wynika to z wprowadzanego do niej tekstu. Uruchom poniższy program:
// Tekst //------ #include <iostream> using namespace std; int main() { setlocale(LC_ALL,""); char a[] = "Miś Uszatek"; cout << a << endl << endl; return 0; } |
Miś Uszatek |
Dostęp do poszczególnych znaków tekstu uzyskujemy standardowo poprzez indeksy. Pierwszy znak tekstu ma indeks 0. Uruchom zmodyfikowany program:
// Tekst //------ #include <iostream> using namespace std; int main() { setlocale(LC_ALL,""); char a[] = "Miś Uszatek"; cout << a << endl; a[0] = 'L'; a[2] = 's'; cout << a << endl << endl; return 0; } |
Miś Uszatek Lis Uszatek |
Zwróć uwagę, iż do komórek tablicy zapisujemy kody znaków. Do tego celu w języku C++ używane są apostrofy. 'L' oznacza kod litery L, czyli 76. Zapis "L" oznacza tekst, czyli literkę L oraz znak NUL, który jest wstawiany na końcu tekstu i w rzeczywistości jest adresem do miejsca w pamięci, w którym kompilator umieścił te litery. Dlatego przypisanie a[0] = "L"; nie ma sensu, ponieważ komórka a[0] może przechować tylko kod znaku, a nie adres tekstu w pamięci. W trakcie przypisania w definicji tablicy:
char a[] = "Miś Uszatek";
komputer wykorzystuje adres tekstu "Miś Uszatek" do skopiowania poszczególnych znaków to komórek tablicy a, gdy ją tworzy. Mógłbyś wpisywać poszczególne kody znaków do tablicy:
char a[] = {'M','i','ś',' ','U','s','z','a','t','e','k','\0'};
lub w postaci liczbowej :
char a[] = {77,105,(signed
char)156,32,85,115,122,97,116,101,107,0};
Przyznasz zatem, że pierwszy sposób jest najprostszy. Na końcu zawsze należy umieścić znak NUL, inaczej tekst ucieknie poza tablicę.
Gdy tablica zostanie zdefiniowana, przypisanie tekstu już nie działa:
// Tekst
//------
#include <iostream>
using namespace std;
int main()
{
setlocale(LC_ALL,"");
char a[100];
a = "Miś Uszatek";
cout << a << endl << endl;
return 0;
}
|
Zatem, aby wprowadzić tekst do tablicy należy wykorzystać funkcję (najlepiej biblioteczną) lub wprowadzać tekst litera po literze do poszczególnych komórek. Funkcje operujące na tablicach znakowych zdefiniowane są w pliku nagłówkowym cstring, który należy dołączyć do programu dyrektywą #include <cstring>. Poznamy teraz kilka z tych funkcji. Wszystkich funkcji jest dużo więcej, możesz je bez problemu znaleźć w Internecie szukając wg haseł c++ cstring.
Nazwa funkcji pochodzi od słów angielskich string length = długość tekstu. Funkcja jako parametr przyjmuje adres tekstu zakończonego znakiem NUL. Jako wynik funkcja zwraca liczbę znaków od początku tekstu do znaku poprzedzającego znak NUL. Składnia funkcji jest następująca:
int strlen(tekst);
Uruchom poniższy program:
// Funkcje tekstowe //----------------- #include <iostream> #include <cstring> using namespace std; int main() { setlocale(LC_ALL,""); cout << "Liczba znaków: " << strlen("Miś Uszatek") << endl << endl; return 0; |
Liczba znaków: 11 |
Tekst w apostrofach jest w C++ adresem w pamięci pierwszego znaku tekstu. Dlatego
możemy go przekazać w parametrze do funkcji
Kolejny program wykorzystuje tablicę znakową:
// Funkcje tekstowe //----------------- #include <iostream> #include <cstring> using namespace std; int main() { setlocale(LC_ALL,""); char a[] = "Miś uszatek i lisek Chytrusek"; cout << "Liczba znaków w \"" << a << "\" = " << strlen(a) << endl << endl; return 0; } |
Liczba znaków w "Miś uszatek i lisek Chytrusek" = 29 |
Tutaj tworzymy tablicę znakową a i wprowadzamy do niej tekst.
Następnie wypisujemy tekst oraz jego długość. Jeśli chcemy wyświetlić cudzysłów,
to poprzedzamy go znakiem \. Inaczej cudzysłów zostanie potraktowany jako znak
końca tekstu, czyli tzw. delimiter. Nazwa tablicy jest adresem jej
pierwszego znaku. Zatem jako parametr
Trzeci program odczytuje dowolny tekst z okna konsoli. Polskie znaki są zamieniane z LATIN2 na kodowanie WIN1 1250, aby były poprawnie wyświetlone w oknie konsoli. Wykorzystujemy tu funkcję PL( ), którą utworzyliśmy w poprzednim podrozdziale.
// Funkcje tekstowe //----------------- #include <iostream> #include <cstring> using namespace std; void PL(char * t) { while(*t) { switch((unsigned char)*t) { case 164 : *t = 165; break; case 143 : *t = 198; break; case 168 : *t = 202; break; case 157 : *t = 163; break; case 227 : *t = 209; break; case 224 : *t = 211; break; case 151 : *t = 140; break; case 141 : *t = 143; break; case 189 : *t = 175; break; case 165 : *t = 185; break; case 134 : *t = 230; break; case 169 : *t = 234; break; case 136 : *t = 179; break; case 228 : *t = 241; break; case 162 : *t = 243; break; case 152 : *t = 156; break; case 171 : *t = 159; break; case 190 : *t = 191; break; default : break; } t++; } } int main() { setlocale(LC_ALL,""); char a[256]; cout << "Wpisz swój tekst: "; cin.getline(a,256); PL(a); cout << "Liczba znaków tekstu \"" << a << "\" = " << strlen(a) << endl << endl; return 0; } |
Wpisz swój tekst: Aligator Józek zjadł swój
wózek Liczba znaków tekstu "Aligator Józek zjadł swój wózek" = 31 |
Nazwa funkcji pochodzi od słów angielskich string copy = kopiowanie tekstu. Składnia funkcji jest następująca:
char *
strcpy(tekst_docelowy,tekst_źródłowy)
Jako argumenty funkcja wymaga dwóch adresów (wskaźników): tekst_docelowy jest adresem tablicy znakowej, do której trafi tekst spod adresu tekst_źródłowy. Tekst źródłowy jest kopiowany znak po znaku do momentu, aż funkcja skopiuje znak NUL. Wtedy kopiowanie się kończy. Należy zadbać, aby tablica docelowa miała wystarczającą pojemność na tekst źródłowy. Dzięki tej funkcji możemy wprowadzać do tablicy znakowej tekst, czyli jest ona ograniczonym odpowiednikiem operacji przypisania. Wynikiem funkcji jest adres tekstu docelowego. Tekst docelowy i tekst źródłowy nie powinny się pokrywać w pamięci. Uruchom poniższy program:
int main() { setlocale(LC_ALL,""); char a[256]; strcpy(a,"Baba Jaga"); cout << a << endl; strcpy(a,"Nietoperz Gacuś"); cout << a << endl; strcpy(a,"Kotek Młotek"); cout << a << endl << endl; return 0; } |
Baba Jaga Nietoperz Gacuś Kotek Młotek |
Nazwa funkcji pochodzi od angielskich słów string catenation = połączenie tekstów. Funkcja wymaga dwóch argumentów, które są wskaźnikami:
char * strcat(tekst_docelowy,tekst_źródłowy);
Tekst źródłowy zostaje dołączony na końcu tekstu docelowego w ten sposób, iż kończący znak NUL tekstu docelowego jest nadpisywany pierwszym znakiem tekstu źródłowego. Kolejne znaki tekstu źródłowego są następnie kopiowane do tekstu docelowego aż do skopiowania znaku NUL. Funkcja zwraca w wyniku wskaźnik do tekstu docelowego. Tablica docelowa powinna posiadać wystarczającą pojemność, aby przechować oba teksty. Uruchom program:
// Funkcje tekstowe //----------------- #include <iostream> #include <cstring> using namespace std; int main() { setlocale(LC_ALL,""); char a[256]; cout << strcpy(a,"Miś Uszatek") << endl; cout << strcat(a," i klapnięte uszko ma") << endl << endl; return 0; } |
Miś Uszatek Miś Uszatek i klapnięte uszko ma |
Zwróć uwagę, iż w programie do strumienia cout przekazywane są wyniki funkcji strcpy( ) i strcat( ). W obu przypadkach jest to po prostu tablica znakowa a.
Aby skorzystać w swoim programie z klasy string należy najpierw dołączyć plik nagłówkowy o nazwie string za pomocą dyrektywy #include <string>. Od tego momentu możemy korzystać z klasy string oraz z funkcji składowych tej klasy. Najpierw zobaczmy prosty przykład:
// Klasa string //------------- #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a; a = "Miś Uszatek"; a += " lubi miodek!"; a += "\n" + a; cout << a << endl << endl; return 0; } |
Miś Uszatek lubi miodek! Miś Uszatek lubi miodek! |
Jak widzisz, zmienna a klasy string zachowuje się jak normalna zmienna. Działa dla niej operator przypisania oraz niektóre operatory modyfikacji. Przeanalizujmy ważniejsze elementy w programie:
#include <string> |
dołączamy plik nagłówkowy z definicją klasy string. Od tego momentu otrzymujemy nowy typ string i możemy tworzyć zmienne klasy string. | |
string a; |
Tworzymy zmienną a klasy string. Nowo utworzona zmienna zawiera tekst pusty. | |
a = "...text1..."; |
Instrukcja przypisania umieszcza w zmiennej a tekst1. | |
a += "...text2..."; |
Instrukcja modyfikacji. Operator dodawania + oznacza dla tekstów łączenie, zatem tekst2 zostanie dopisany na koniec tekstu, który już znajduje się w zmiennej a. W rezultacie w zmiennej a otrzymamy połączone ze sobą tekst1 i tekst2. | |
a += "\n" + a; |
Na koniec połączonych tekstów w zmiennej a zostaje wstawiony znak końca wiersza /n oraz poprzednia zawartość zmiennej a. W efekcie tekst w a zostanie zdublowany. | |
cout << a << endl << endl; |
Przesłanie zmiennej a do strumienia cout spowoduje wyświetlenie w oknie konsoli jej zawartości. Będą to dwa wiersze, ponieważ pomiędzy nimi został wstawiony znak końca wiersza \n. |
Już ten prosty przykład pokazuje, iż zmienna typu string jest prostsza w obsłudze od tablicy znakowej char, dlatego w dalszej części tego kursu będziemy używali wyłącznie klasy string do tekstów.
Zmienna string może być zainicjowana określoną treścią już w momencie tworzenia:
string zmienna("...tekst...");
// Klasa string //------------- #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a("Cześć, tu string :)"); cout << a << endl << endl; return 0; } |
Cześć, tu string :) |
Wygląda to jak wywołanie funkcji i faktycznie nim jest. Gdy zmienna klasy jest tworzona w pamięci komputera, zostaje wywołana specjalna funkcja składowa tej klasy zwana konstruktorem. Zadaniem konstruktora jest odpowiednie przygotowanie utworzonego obiektu do pracy w programie. Standardowy konstruktor ustawia długość tekstu w zmiennej string na 0, czyli na tekst pusty, który nie zawiera żadnego znaku. Zmieniamy to podając w parametrze dla konstruktora tekst, który chcemy, aby znalazł się w zmiennej od początku jej istnienia. Wszystko to omówimy dokładnie przy klasach C++ w dalszej części kursu.
Można też użyć operatora przypisania, jak w normalnych zmiennych:
string a = "Cześć, tu string :)";
Dostęp do poszczególnych znaków w zmiennej string uzyskuje się przy pomocy indeksów tak samo jak dla tablicy.
Liczbę znaków (a właściwie liczbę bajtów, jednak dla kodu ASCII jest to to samo) zwraca funkcja składowa lenght( ). Funkcję składową klasy zawsze wywołujemy z nazwą zmiennej oddzieloną operatorem kropka:
zmienna_string.length();
Kolejny program odczytuje jeden wyraz ze strumienia cin i umieszcza go w zmiennej typu string. Następnie program wyświetla liczbę znaków w wyrazie oraz kolejne znaki wyrazu i ich kody. W programie umieściliśmy zmodyfikowaną dla klasy string funkcję PL( ), która zamienia znaki w kodzie LATIN2 na kod WIN 1250.
// Klasa string //------------- #include <iostream> #include <string> using namespace std; void PL(string & t) { unsigned i,len = t.length(); for(i = 0; i < len; i++) switch((unsigned char) t[i]) { case 164 : t[i] = 165; break; case 143 : t[i] = 198; break; case 168 : t[i] = 202; break; case 157 : t[i] = 163; break; case 227 : t[i] = 209; break; case 224 : t[i] = 211; break; case 151 : t[i] = 140; break; case 141 : t[i] = 143; break; case 189 : t[i] = 175; break; case 165 : t[i] = 185; break; case 134 : t[i] = 230; break; case 169 : t[i] = 234; break; case 136 : t[i] = 179; break; case 228 : t[i] = 241; break; case 162 : t[i] = 243; break; case 152 : t[i] = 156; break; case 171 : t[i] = 159; break; case 190 : t[i] = 191; break; default : break; } } int main() { setlocale(LC_ALL,""); string a; cout << "Wpisz pojedynczy wyraz: "; cin >> a; PL(a); // Korekcja odczytanych kodów cout << endl << "Liczba znaków : " << a.length() << endl << endl; for(unsigned i = 0; i < a.length(); i++) cout << "Znak: " << a[i] << " kod: " << (int)(unsigned char)a[i] << endl; cout << endl; return 0; } |
Wpisz pojedynczy wyraz: książę Liczba znaków : 6 Znak: k kod: 107 Znak: s kod: 115 Znak: i kod: 105 Znak: ą kod: 185 Znak: ż kod: 191 Znak: ę kod: 234 |
Przeanalizujmy ważniejsze elementy tego programu:
string a; |
Tworzymy zmienną a klasy string. Zmienna a nie zawiera żadnego tekstu. | |
cin >> a; |
Do zmiennej a odczytujemy jeden wyraz znaków ze strumienia cin. Jeśli wprowadzisz więcej niż jeden wyraz, ze strumienia cin zostanie odczytany tylko pierwszy z nich. Spacja jest traktowana jako koniec wyrazu. Spacje początkowe są ignorowane. | |
cout ... << a.length() |
Do strumienia wyjściowego cout przekazujemy liczbę znaków w zmiennej a, czyli w odczytanym wyrazie. | |
for(unsigned i=0;i<a.length();i++) |
Pętla for wykona się tyle razy, ile wynosi liczba znaków w
a. Zmienna i przebiegnie indeksy wszystkich znaków w a.
Jeśli chcesz przyspieszyć działanie tej pętli, to liczbę znaków
zapamiętaj w osobnej zmiennej i ją użyj w porównaniu. Dzięki temu
komputer nie będzie musiał liczyć znaków w a w każdym obiegu
pętli. Tak zrobiliśmy w funkcji PL( ):unsigned i, len = a.length(); for(i = 0; i < len; i++)... |
|
cout ... << a[i] ... |
Do strumienia cout trafia znak ze zmiennej a, który znajduje się na pozycji i-tej. | |
cout ... << (int)(unsigned char)a[i] ... |
Ta dziwna konstrukcja ma za zadanie przesłanie do strumienia cout kodu znaku jako liczby nieujemnej 8-bitowej. Dokonujemy tego dwoma rzutowaniami, które należy czytać od strony prawej do lewej. Najpierw znak a[i] zostaje zamieniony na znak typu unsigned char, a następnie ten znak zmieniany jest w typ int. W efekcie dla kodów rozszerzonych otrzymujemy liczbę dodatnią z zakresu od 128 do 255. Bez rzutowania kody rozszerzone ASCII byłyby ujemne, ponieważ prosty typ char jest 8-bitowym odpowiednikiem typu int i jest interpretowany jako 8-bitowa liczba U2. |
Jeśli chcemy odczytać cały wiersz ze strumienia, używamy funkcji getline( ), jednak w tym przypadku funkcja ta należy do klasy string i nie jest funkcją składową strumienia cin. Jest to inna funkcja, posiada ona jedynie taką samą nazwę:
Odczyt wiersza do tablicy char:
cin.getline(tablica_char,liczba_znaków);
Odczyt wiersza do zmiennej klasy string:
getline(cin,zmienna_string,{delimiter})
To
częsta praktyka w języku C++. Mogą istnieć różne funkcje o tej samej nazwie tak,
jak mogą istnieć różni uczniowie o tym samym nazwisku lub imieniu. Funkcja
cin | Strumień wejścia, z którego będą odczytywane znaki tworzące wiersz. W tym miejscu może pojawić się dowolny inny strumień wejścia, niekoniecznie musi to być cin – w ten sposób możemy odczytywać wiersz z różnych urządzeń, np. z pliku na dysku czy z pamięci komputera, co zobaczymy później. | |
zmienna_string | Zmienna klasy string, w której zostanie umieszczony wiersz znaków. Nie ma tutaj ograniczenia na długość wiersza, gdyż zmienne string dynamicznie dopasowują się do ilości danych. | |
{delimiter} | Ten parametr nie jest obowiązkowy. Możesz go nie podawać w wywołaniu funkcji. Jeśli go pominiesz, to wiersz zostanie wczytany do momentu napotkania w strumieniu znaku końca wiersza \n. W przeciwnym razie odczyt będzie następował aż do napotkania znaku delimitera. W obu przypadkach znak kończący wiersz (\n lub delimiter) jest usuwany ze strumienia, lecz nie jest wstawiany do zmiennej string. |
Uruchom poniższy program. Odczytuje on wiersz znaków, po czym wypisuje go wspak.
// Klasa string //------------- #include <iostream> #include <string> using namespace std; void PL(string & t) { unsigned i,len = t.length(); for(i = 0; i < len; i++) switch((unsigned char) t[i]) { case 164 : t[i] = 165; break; case 143 : t[i] = 198; break; case 168 : t[i] = 202; break; case 157 : t[i] = 163; break; case 227 : t[i] = 209; break; case 224 : t[i] = 211; break; case 151 : t[i] = 140; break; case 141 : t[i] = 143; break; case 189 : t[i] = 175; break; case 165 : t[i] = 185; break; case 134 : t[i] = 230; break; case 169 : t[i] = 234; break; case 136 : t[i] = 179; break; case 228 : t[i] = 241; break; case 162 : t[i] = 243; break; case 152 : t[i] = 156; break; case 171 : t[i] = 159; break; case 190 : t[i] = 191; break; default : break; } } int main() { setlocale(LC_ALL,""); string a; cout << "Wpisz poniżej wiersz znaków:" << endl; getline(cin,a); PL(a); for(unsigned i = a.length(); i > 0; i--) cout << a[i - 1]; cout << endl << endl; return 0; } |
Wpisz poniżej wiersz znaków: Zażółć żabią jaźń, książę! !ężąisk ,ńźaj ąibaż ćłóżaZ |
obiekt.element_składowy;
obiekt.funkcja_składowa(argumenty);
Omówimy teraz kilka przydatnych funkcji składowych zmiennych klasy string.
Tę funkcję już poznaliśmy. Zwraca ona liczbę bajtów zawartych w zmiennej klasy string. Dla kodu ASCII będzie to to samo, co liczba znaków (możliwe jest również inne kodowanie, ale tym sobie nie zawracajmy na początku głowy). Funkcja length( ) przydaje się, gdy chcemy poznać długość tekstu przechowywanego w zmiennej typu string. Zmienne klasy string nie mają ograniczeń na długość tekstu.
Uruchom program:
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a = "ABC:"; do { cout << "Długość : " << a.length() << endl << "Tekst w a: " << a << endl << endl; a += "X"; } while(a.length() <= 10); return 0; } |
Długość : 4 Tekst w a: ABC: Długość : 5 Tekst w a: ABC:X Długość : 6 Tekst w a: ABC:XX Długość : 7 Tekst w a: ABC:XXX Długość : 8 Tekst w a: ABC:XXXX Długość : 9 Tekst w a: ABC:XXXXX Długość : 10 Tekst w a: ABC:XXXXXX |
Funkcja zmienia rozmiar tekstu w zmiennej string. Posiada jeden lub dwa parametry:
zmienna_string.resize(rozmiar);
zmienna_string.resize(rozmiar,znak);
Pierwsza postać używana jest najczęściej do zmniejszania długości tekstu. Rozmiar określa nową długość tekstu w zmiennej. Jeśli rozmiar jest mniejszy od aktualnej długości tekstu w zmiennej, to tekst zostanie obcięty do rozmiaru. Jeśli rozmiar jest większy od długości tekstu, to tekst zostanie powiększony do rozmiaru przez dodanie na końcu znaków NUL, które są traktowane w zmiennej string jako normalne znaki i nie oznaczają końca tekstu jak w tablicach char. Uruchom program:
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a = "Kubuś i Prosiaczek"; cout << a << endl; a.resize(5); cout << a << "?" << endl << endl; return 0; } |
Kubuś i Prosiaczek Kubuś? |
Poeksperymentuj z tym programem zwiększając rozmiar.
Druga postać posiada dodatkowo parametr znak. Jeśli tekst ma więcej znaków niż rozmiar, to zostanie obcięty do rozmiaru. Jeśli tekst ma mniej znaków niż rozmiar, to zostanie uzupełniony podanym znakiem aż do osiągnięcia rozmiaru. Uruchom poniższy program:
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a = "Programowanie w języku C"; cout << a << endl; a.resize(a.length() + 2,'+'); cout << a << endl; a.resize(a.length() - 13,'-'); cout << a << endl << endl; return 0; } |
Programowanie w języku C Programowanie w języku C++ Programowanie |
Funkcja zwraca wartość logiczną true, jeśli tekst zawarty w zmiennej string jest pusty (tzn. nie zawiera żadnego znaku). W przeciwnym razie zwracane jest false.
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; void PL(string & t) { unsigned i,len = t.length(); for(i = 0; i < len; i++) switch((unsigned char) t[i]) { case 164 : t[i] = 165; break; case 143 : t[i] = 198; break; case 168 : t[i] = 202; break; case 157 : t[i] = 163; break; case 227 : t[i] = 209; break; case 224 : t[i] = 211; break; case 151 : t[i] = 140; break; case 141 : t[i] = 143; break; case 189 : t[i] = 175; break; case 165 : t[i] = 185; break; case 134 : t[i] = 230; break; case 169 : t[i] = 234; break; case 136 : t[i] = 179; break; case 228 : t[i] = 241; break; case 162 : t[i] = 243; break; case 152 : t[i] = 156; break; case 171 : t[i] = 159; break; case 190 : t[i] = 191; break; default : break; } } int main() { setlocale(LC_ALL,""); string a; cout << "Wprowadź poniżej wiersz tekstu:" << endl; getline(cin,a); PL(a); cout << endl; while(!a.empty()) // Dopóki tekst NIE pusty { a.resize(a.length()-1); cout << a << "***" << endl; } cout << endl; return 0; } |
Wprowadź poniżej wiersz tekstu: Zażółć żabią jaźń Zażółć żabią jaź*** Zażółć żabią ja*** Zażółć żabią j*** Zażółć żabią *** Zażółć żabią*** Zażółć żabi*** Zażółć żab*** Zażółć ża*** Zażółć ż*** Zażółć *** Zażółć*** Zażół*** Zażó*** Zaż*** Za*** Z*** *** |
Czyści zmienną string, tzn. usuwa cały tekst zawarty w zmiennej. Tę samą operację można wykonać przypisując zmiennej tekst pusty:
zmienna_string = "";
Uruchom program:
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a = "Krokodyl Arek"; cout << "a = \"" << a << "\", liczba znaków = " << a.length() << endl; a.clear(); // Usuwamy tekst ze zmiennej cout << "a = \"" << a << "\", liczba znaków = " << a.length() << endl << endl; return 0; } |
a = "Krokodyl Arek", liczba znaków = 13 a = "", liczba znaków = 0 |
Umożliwia dostęp do znaku w zmiennej string na pozycji, którą podamy jako parametr. Funkcja ta dokładnie odpowiada indeksowaniu w klamerkach:
zmienna_string.at(pozycja) <---> zmienna_string[pozycja]
Dostęp do znaku oznacza, iż program może go odczytać, np:
cout <<
zmienna_string.at(pozycja) ...
lub dowolnie zmodyfikować, np:
zmienna_string.at(pozycja) = 'nowy znak';
Uruchom program:
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a = "Ala ma chomika"; cout << a << endl; for(int i = a.length()-1; i >= 0; i--) cout << a.at(i); cout << endl << endl; return 0; } |
Ala ma chomika akimohc am alA |
Zmień w programie wywołanie a.at(i) na a[i].
Funkcja daje dostęp do ostatniego znaku tekstu przechowywanego w zmiennej
string. zmienna_string.back()
jest odpowiednikiem:
zmienna_string.at(zmienna_string.length()-1)
lub
zmienna_string[zmienna_string.length()-1]
Funkcji tej nie należy używać z tekstami pustymi.
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a = "Jaś"; cout << a << endl; a.back() = 'n'; cout << a << endl; return 0; } |
Jaś Jan |
Funkcja daje dostęp do pierwszego znaku tekstu w zmiennej string. Jest ona odpowiednikiem:
zmienna_string.at(0)
lub
zmienna_string[0]
Funkcji tej nie należy używać z tekstami pustymi.
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { setlocale(LC_ALL,""); string a = "jeść"; cout << a << endl; a.front() = 't'; cout << a << endl; return 0; } |
jeść teść |
Funkcja jako parametr przyjmuje znak, który zostanie dołączony na końcu bieżącego tekstu w zmiennej string. W efekcie długość tekstu wzrasta o 1.
zmienna_string.push_back(znak)
Usuwa z tekstu w zmiennej string ostatni znak. W efekcie długość tekstu zmniejsza się o 1.
zmienna_string.pop_back()
// Funkcje składowe string //------------------------ #include <iostream> #include <string> using namespace std; int main() { string a; for(char i = 'a'; i <= 'z'; i++) a.push_back(i); do { cout << a << endl; a.pop_back(); } while(!a.empty()); return 0; } |
abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxy abcdefghijklmnopqrstuvwx abcdefghijklmnopqrstuvw abcdefghijklmnopqrstuv abcdefghijklmnopqrstu abcdefghijklmnopqrst abcdefghijklmnopqrs abcdefghijklmnopqr abcdefghijklmnopq abcdefghijklmnop abcdefghijklmno abcdefghijklmn abcdefghijklm abcdefghijkl abcdefghijk abcdefghij abcdefghi abcdefgh abcdefg abcdef abcde abcd abc ab a |
Resztę funkcji składowych poznamy w miarę potrzeb.
zmienna_string.length()
.zmienna_string.resize(rozmiar)
i
zmienna_string.resize(rozmiar,znak)
.zmienna_string.empty()
.zmienna_string.clear()
. Jak można inaczej
zrealizować tę operację?zmienna_string.at(pozycja)
. Jak można
inaczej zrealizować tę operację?zmienna_string.back()
. Jak można inaczej
zrealizować tę operację?zmienna_string.front()
. Jak można inaczej
zrealizować tę operację?zmienna_string.push_back(znak)
. Jak można
inaczej zrealizować tę operację?zmienna_string.pop_back()
. Jak można
inaczej zrealizować tę operację?![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2023 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.