LEKCJA 18 - O ŁAŃCUCHACH TEKSTOWYCH
________________________________________________________________
W trakcie tej lekcji dowiesz się,
* jak manipulować łańcuchami tekstowymi i poznasz kilka
specjalnych funkcji, które służą w C++ właśnie do takich celów;
* jak wykonują się operacje plikowo-dyskowe.
________________________________________________________________
OPERACJE NA ŁAŃCUCHACH TEKSTOWYCH.
String, czyli łańcuch - to gupa znaków "pisarskich" (liter, cyfr
i znaków specjalnych typu ?, !, _ itp.). Ponieważ C++ nie ma
odzielnego typu danych "string" - łańcuchy znaków to tablice
złożone z pojedynczych znaków (typowe elementy typu char).
Techniką obiektową można utworzyć klasę - nowy typ danych
"string". W bibliotekach Microsoft C++ istnieje predefiniowana
klasa CString, ale zanim przejdziemy do programowania
obiektowego i zdarzeniowego - rozważmy manipulowanie tekstami w
sposób najprostszy.
Maksymalną możliwą długość napisu należy podać wtedy, gdy w
programie deklaruje się zmienną tekstową:
char tekst1[40];
Jest to poprawna deklaracja zmiennej tekstowej o nazwie
(identyfikator) tekst1. Maksymalna długość tekstu, który można
umieścić w tej zmiennej tekstowej to - 40 znaków (liter, cyfr,
itp.). A jeśli chcę zastosować tylko pojedynczy znak zamiast
całego napisu? To proste:
char napis[1];
Skoro długość łańcucha wynosi 1, to przecież nie jest żaden
łańcuch! Informacja o długości (size - wielkość) wpisywana w
nawiasy jest zbędna. Uproszczona wersja utworzenia zmiennej
jednoznakowej i nadania zmiennej nazwy wygląda w tak:
char znak;
Nie jest to już jednak deklaracja zmiennej łańcuchowej - lecz
deklaracja zmiennej znakowej. Łańcuch znaków (string) to grupa
znaków (dokł. tablica znakowa) zakończona zwykle przez tzw.
"wartownika" - znak NULL (zero). A pojedynczy znak to tylko
pojedynczy znak. Nie ma tu miejsca (i sensu) dodawanie po
pojedynczym znaku "wartownika" końca tekstu - zera.
Gdy w deklaracjach zmiennych tekstowych rezerwujesz miejsce w
pamięci dla napisów - zawsze możesz zażądać od kompilatora C++
zarezerwowania większej ilości miejsca - na zapas. Zawsze lepiej
mieć zbyt dużo miejsca, niż zbyt mało.
[???] LEPIEJ MIEĆ NIŻ NIE MIEĆ.
________________________________________________________________
Upewnij się, czy kompilator zarezerwował (a Ty zadeklarowałeś)
wystarczająco dużo miejsca dla Twoich tekstów. C++ niestety nie
sprawdza tego w trakcie działania programu. Jeśli będziesz
próbował umieścić w pamięci tekst o zbyt dużej długości (dłuższy
niż zadeklarowałeś w programie), C++ posłusznie zapisze go do
pamięci, ale może to spowodować nieprawidłowe działanie, bądź
nawet "zawieszenie" programu.
________________________________________________________________
Inną przydatną w praktyce programowania cechą języka C++ jest
możliwość zadeklarowania zawartości zmiennej tekstowej w
momencie zadeklarowania samej zmiennej. Takie nadanie
początkowej wartości nazywa się zdefiniowaniem, bądź
zainicjowaniem zmiennej. W programie zapisuje się to tak:
char napis[] = "To jest jakis napis";
Powoduje to przypisanie zmiennej tekstowej "napis" konkretnego
łańcucha tekstowego "To jest jakiś napis". Zwróć uwagę, że w
nawiasach nie podajemy ilości znaków, z których składa się
tekst. Kompilator sam policzy sobie ilość znaków (tu 19) i
zarezerwuje miejsce w pamięci dla napisu. Jeśli wolisz sam
zadecydować, możesz zapisać deklarację tak:
char napis[35] = "To jest jakis napis";
Jeśli to zrobisz, kompilator C++ zarezerwuje w pamięci miejsce
dla 35 znaków, a nie dla 19.
W programach często inicjuje się teksty posługując się nie
tablicą znakową - lesz wskaźnikiem do tekstu. Deklaracja i
zainicjowanie wskaźnika (wskaźnik wskazuje pierwszy element
łańcucha znakowego) wygląda wtedy tak:
char *p = "Jakis tam napis";
Rzućmy okiem na kilka gotowych funkcji, które do manipulowania
tekstami oferuje C++.
ŁĄCZENIE TEKSTÓW.
[S] String Concatenation - łączenie łańcuchów tekstowych.
Zlepek/skrót. Słowo strcat w języku C++ znaczy sklej.
W praktycznych programach zapewne często pojawi się dwa lub
więcej tekstów, które trzeba będzie połączyć w jeden napis.
Wyobraźmy sobie, że imię i nazwisko użytkownika mamy zapisane
jako dwa oddzielne łańcuchy tekstowe. Aby połączyć te dwa teksty
w jeden trzeba przeprowadzić tzw. sklejanie (ang. concatenation)
tekstów. W języku C++ mamy w tym celu do dyspozycji specjalną
funkcję:
strcat() - STRing conCATenation - sklejanie łańcuchów.
Aby połączyć dwa łańcuchy tekstowe napis1 i napis2 w jeden
należy zastosować tę funkcję w taki sposób:
strcat(napis1, napis2);
Funkcja strcat() zadziała w taki sposób, że łańcuch znaków
napis2 zostanie dołączony do końca łańcucha napis1. Po
zakończeniu działania funkcji zmienna napis1 zawiera "swój
własny" napis i dołączony na końcu napis zawarty uprzednio w
zmiennej napis2.
Program poniżej przedstawia praktyczny przykład zastosowania
funkcji strcat().
[P066.CPP]
#include
#include
#include //W tym pliku jest prototyp strcat()
int main(void)
{
char imie[50], nazwisko[30];
clrscr();
cout << "Podaj imie: ";
cin >> imie;
cout << "Podaj nazwisko: ";
cin >> nazwisko;
strcat(imie, " ");
strcat(imie, nazwisko);
cout << "\nNazywasz sie: " << imie << '\n';
cout << "Naciśnij dowolny klawisz";
getch();
return 0;
}
Program zapyta najpierw o imię a następnie o nazwisko. Po
wpisaniu przez Ciebie odpowiedzi program doda do siebie oba
teksty i wypisze na ekranie Twoje imię i nazwisko w całości.
Interesująxe w programie jest połączenie przy pomocy funkcji C++
strcat() dwu łańcuchów tekstowych w jeden łańcuch z dodaniem
spacji rozdzielającej łańcuchy znaków. Najistotniejszy fragment
programu wraz z komentarzem - poniżej.
strcat(imie, " "); <-- dodaj do końca tekstu spację
strcat(imie, nazwisko); <-- po dołączonej spacji dodaj
drugi tekst - nazwisko
Ponieważ prototyp funkcji strcat() znajduje się w pliku STRING.H
- należy dołączyć ten plik nagłówkowy dyrektywą #include.
DŁUGOŚĆ ŁAŃCUCHA TEKSTOWEGO.
Każdy tekst ma swoją długość: liczbę znaków, z których się
składa. Dla przykładu łańcuch znaków:
"Przychodzi katecheta do lekarza i płacze, a lekarz na to: Bóg
dał - Bóg wziął..."
ma dla długość 71, ponieważ składa się z 71 znaków (odstęp -
spacja to też znak). Łańcuch znaków
"Ile diabłów mieści się w łebku od szpilki?"
ma długość 42. Teoretycznie długość łańcuchów znakowych może
wynosić od 0 do nieskończoności, ale w Borland/Turbo C++
występuje ograniczenie: łańcuch znaków może mieć długość zawartą
w przedziale od 0 do 65536 znaków. Taki np. łańcuch znaków jest
całkiem do przyjęcia:
"Nie ważne, czy Polska będzie bogata, czy biedna - ważne, żeby
była katolicka (czyli nasza), bo nasze będą wtedy pieniądze,
urzędy i nasza władza. Lepiej być pół-Bogiem wśród nędzarzy
(oczywiście za ich pieniądze, z ich podatków), niż zarabiać na
chleb własną pracą."
[S] Null string - Łańcuch zerowy.
________________________________________________________________
Łańcuch zerowy (dokładniej: łańcuch tekstowy o zerowej długości)
to taki łańcuch, który zawiera 0 (zero) znaków. Jak to możliwe,
by łańcuch tekstowy zawierał zero znaków? W C++ łańcuchy znaków
zawierają na końcu znak '\0' (zero) jako "wartownika" końca
tekstu. Jeśli pierwszy element tablicy znakowej będzie zerem -
powstanie właśnie łańcuch znakowy o zerowej długości. Można to
zrobić np. tak:
char napis[0] = 0;
char *p = "";
char napis[50] = "";
________________________________________________________________
Kiedy C++ wyznacza długość łańcucha znaków - zlicza kolejne
znaki, aż dojdzie do zera. W przykładzie już pierwszy znak jest
zerem, więc C++ uzna, że długość takiego łańcucha wynosi zero.
Czasem w praktyce programowania zainicjowanie takiego pustego
łańcucha pozwala mieć pewność, że tablica nie zawiera jakichś
starych, zbędnych danych.
Możliwość sprawdzenia, jaką długość ma łańcuch tekstowy może się
to przydać np. do rozmieszczenia napisów na ekranie. Dla
przykładu, pozycja na ekranie, od której rozpocznie się
wyświetlanie napisu zależy od długości tekstu, który został
wyświetlony wcześniej. Do określania długości tekstu masz w C++
do dyspozycji gotową funkcję:
strlen() - STRing LENgth - długość łańcucha znakowego.
Funkcję strlen() stosuje się w następujący sposób:
unsigned int dlugosc;
char tekst[...];
...
dlugosc = strlen(tekst);
Funkcja ma jeden argument - napis, którego długość należy
określić (tu: zmienna nazywa się tekst). Funkcja strlen() w
wyniku swojego działania ZWRACA długość łańcucha tekstowego jako
liczbę całkowitą bez znaku (nieujemną). Liczba zwrócona jako
wynik przez funkcję strlen() może zostać użyta w dowolny sposób
- jak każda inna wartość numeryczna.
Funkcja strlen() nie podaje w odpowiedzi na wywołanie (mądrze
nazywa się to "zwraca do programu wartość") długości łańcucha
tekstowego, która została zadeklarowana (maksymalnej
teoretycznej), lecz FAKTYCZNĄ DŁUGOŚĆ tekstu. Jeśli, dla
przykładu, zadeklarujemy zmienną tekstową tak:
char string1[30] = "Lubie C++ ";
zadeklarowana maksymalna długość łańcucha znakowego wynosi 30,
natomiast faktyczna długość łańcucha znakowego wynosi 10 znaków.
Jeśli wywołamy strlen() i każemy jej określić długość łańcucha
znakowego string1:
unsigned int dlugosc = strlen(string1);
funkcja przypisze zmiennej dlugosc wartość 10 a nie 30.
Jeśli wpisałeś poprzedni program program przykładowy do okienka
edycyjnego - wystarczy dodać dwa nowe wiersze.
[P067.CPP]
#include
#include
#include
main()
{
char imie[50], nazwisko[20];
int dlugosc;
clrscr();
cout << "Podaj imie: ";
cin >> imie;
cout << "Podaj nazwisko: ";
cin >> nazwisko;
strcat(imie, " ");
strcat(imie, nazwisko);
cout << "\nNazywasz sie: " << imie << '\n';
dlugosc = strlen(imie);
cout<<"Imie i nazwisko sklada sie z: "<
#include
#include
#include
main()
{
char napis1[80] = "";
char napis2[80] = "";
char napis3[80] = "";
clrscr();
cout << "Wpisz jakis tekst: ";
gets(napis1);
strcpy(napis2, napis1);
strncpy(napis3, napis1, 3);
cout << "\nKopia tekstu: ";
cout << '*' << napis2 << "*\n";
cout << "Pierwsze 3 znaki tekstu: ";
cout << '\'' << napis3 << '\'' << '\n';
cout << "\n\n...dowolny klawisz...";
getch();
return 0;
}
[???] A jeśli zabraknie znaków?
________________________________________________________________
Spróbuj uruchomić program podając mu łańcuch tekstowy krótszy
niż 5 znaków. Jest to próba oszukania funkcji, która oczekuje,
że kopiowane 3 znaki powinny istnieć, mało tego, powinny być
zaledwie częścią większego łańcucha.
Jak widzisz, program nie "zawiesza się". W języku C++ funkcje
opracowane są zwykle w taki sposób, że nawet otrzymując
bezsensowne parametry potrafią jakoś tam wybrnąć z sytuacji. Tym
niemniej, nawet jeśli program się nie zawiesza, nie oznacza to,
że wyniki działania przy bezsensownych danych wejściowych będą
mieć jakikolwiek sens. Jako programista powinieneś wystrzegać
się takich błędów (dane z poza zakresu, dane bez sensu
merytorycznego) nie licząc na to, że C++ jakoś z tego wybrnie.
________________________________________________________________
Najważniejszy fragment tekstu programu wraz z komentarzem:
char napis1[80] = ""; <-- deklaracje zmiennych tekstowych
char napis2[80] = ""; <-- i nadanie im zerowej zawartości
char napis3[80] = ""; <-- długość pustego napisu - zero.
...
gets(napis1); <-- GET String - pobierz string
strcpy(napis2, napis1); <-- kopiowanie całego tekstu
strncpy(napis3, napis1, 3); <-- kopiowanie części tekstu
...
Zwróć uwagę, że program do pobrania danych (tekstu) od
użytkownika posługuje się funkcją gets() (ang. GET String -
pobierz łańcuch znaków). Obiekt cin jest bardzo wygodnym
środkiem służącyn do wczytywania danych, ale nie pozwala
wprowadzać napisów zawierających spacje. Jeśli zastosowalibyśmy
w programie
cin >> string1;
i wpisali tekst zawierający spacje, np.:
To nie ważne, czy Polska...
wczytane zostałyby tylko znaki To (do pierwszej spacji). Z kolei
funkcja gets() pozwala wczytać wiersz tekstu zawierający dowolne
znaki uznając za koniec znak CRLF (powrót karetki, zmiana
wiersza) generowany po naciśnięciu [Entera]. Przeciwną,
symetryczną funkcją do gets() jest funkcja puts() (ang. PUT
String - wyprowadź wiersz tekstu). Prototypy funkcji gets() i
puts() znajdują się w pliku nagłówkowym STDIO.H. Dlatego ten
plik nagłówkowy został dołączony na początku dyrektywą #include.
WYSZUKIWANIE TEKSTÓW.
Wyobraźmy sobie, że mamy listę imion i chcemy na tej liście
odszukać znajome imię np. Alfons. Specjalnie do takich celów C++
dysponuje funkcją:
strstr() - STRing's subSTRing - część łańcucha tekstowego
Aby wyszukać w większym tekście mniejszy fragment, powinniśmy
wywołując funkcję przekazać jej dwie informacje:
GDZIE SZUKAĆ - wskazać łańcuch tekstowy do przeszukiwania;
i
CZEGO SZUKAĆ - podać ten tekst, który nas interesuje i który
funkcja powinna dla nas odnaleść.
Funkcja strstr(), powinna zatem mieć dwa argumenty:
char Lista[] = "Adam, Buba, Adolf, Magda";
...
gdzie = strstr(Lista, "Adolf");
Funkcja strstr() wyszukuje pierwsze wystąpienie danego tekstu.
Po wyszukaniu, funkcja powinna nam w jakiś sposób wskazać, gdzie
znajduje się interesujący nas tekst. Jak wiesz, do wskazywania
różnych interesujących rzeczy służą w C++ WSKAŹNIKI (pointer). W
przykładzie powyżej funkcja strstr() w wyniku swojego działania
zwraca wskaźnik do szukanego tekstu "Alfons". Aby wskaźnik nam
nie przepadł, trzeba go zapamiętać. Funkcja zatem przypisuje
wskaźnik zmiennej "gdzie". W miejscu przeznaczonym dla tej
zmiennej w pamięci będzie odtąd przechowywany wskaźnik,
wskazujący nam - gdzie w pamięci kmputera znajduje się
interesujący nas tekst "Alfons\0".
Aby komputer zarezerwował miejsce w pamięci dla wskaźnika,
trzeba go o to "poprosić" na początku programu, deklarując, że w
programie zamierzamy posługiwać się wskaźnikiem. Deklaracja
wskaźnika do zmiennej tekstowej wygląda tak:
char *wskaznik;
Przykładowy program pniżej demonstruje sposób zadeklarowania
wskaźnika i wyszukiwanie tekstu. Program nie oczekuje żadnej
informacji wejściowej od użytkownika. Uruchom program i
przeanalizuj wydruk na ekranie porównując go z tekstem programu.
[P069.CPP]
#include
#include
#include
main()
{
char string1[] = "Ala, Magda, Adam, Alfons, Jasiek, Alfons, As";
char *pointer;
clrscr();
cout << "Lista:\n" << string1;
pointer = strstr(string1, "Alfons");
cout << "Tekst 'Alfons' wystapil po raz pierwszy:\n";
cout << " " << pointer << '\n';
pointer = strstr(ptr, "Jasiek");
cout << "Tekst 'Jasiek' wystapil po raz pierwszy:\n";
cout << " " << pointer << '\n';
pointer = strstr(pointer, "As");
cout << "Tekst 'As' wystapil:\n";
cout << " " << ptr << '\n' << "\n\nNacisnij cokolwiek";
getch();
return 0;
}
Inną metodą zastosowania funkcji manipulujących łańcuchami
tekstowymi może być "obróbka" tekstu wprowadzonego przez
użytkownika. Następny program przykładowy pozwala użytkownikowi
wprowadzić tekst do przeszukiwania (odpowiednik listy) i tekst
do wyszukania (odpowiednik imienia). W wyniku wyszukania
wskazanego łańcucha program wyświetla listę począwszy od
wyszukanego pierwszego wystąpienia zadanego łańcucha znaków.
[P070.CPP]
#include
#include
#include
#include
main()
{
char str1[80], str2[80];
char *ptr;
clrscr();
cout << "Wpisz tekst do przeszukania:\n ";
gets(str1);
cout << "Co mam wyszukac?\n--> ";
gets(str2);
ptr = strstr(str1, str2); <-- wyszukiwanie tekstu
cout << "Znalazlem: " << '\'' << str1 << '\'' << " w ";
cout << '\'' << str2 << '\'' << '\n';
cout << ptr;
cout << "\n\n ...Nacisnij klawisz...";
getch();
return 0;
}
DUŻE I MAŁE LITERY.
Litery mogą być małe i duże. Duże litery nazywają się
"capitals". Od słowa CAPitalS pochodzi skrót na klawiszu [Caps
Lock]. Innym, używanym do określenia tego samego słowem jest
"upper case" (duże litery) lub "lower case" (małe litery).
Czasami pojawia się potrzeba zaminy dużych liter na małe, bądź
odwrotnie. W C++ służą tego celu funkcje:
strupr() - STRing to UPpeR case - zamień litery włańcuchu
tekstowym na duże.
strlwr() - STRing to LoWeR case - zamień litery w łańcuchu na
małe.
Program przykładowy poniżej demonstruje działanie tych funkcji.
[P071.CPP]
#include
#include
#include
#include
main()
{
char string1[80];
clrscr();
cout << "Wpisz tekst do zamiany:\n";
gets(string1);
cout << "\nNormalnie: " << string1 << '\n';
cout << "TYLKO DUZE: " << strupr(string1) << '\n';
cout << "tylko male: " << strlwr(string1) << '\n';
cout << "\n\n...Nacisnij klawisz...";
getch();
return 0;
}
[???] DLA DOCIEKLIWYCH.
________________________________________________________________
* Argumenty funkcji - zawsze w tej samej kolejności!
Kiedy wywołujesz gotową funkcję - np. strstr(), argumenty
funkcji muszą być podane zawsze w tej samej kolejności (tak, jak
funkcja "się spodziewa"). Wywołanie funkcji:
pointer = strstr(string, substring, 3);
powiedzie się i funkcja zadziała zgodnie z oczekiwaniami.
Natomiast wywołanie funkcji tak:
pointer = strstr(3, substring, string);
spowoduje błąd przy kompilacji programu.
* Przy manipulacji stringami kłopoty mogą sprawiać spacje, bądź
ich brak. Dla przykładu przy sklejaniu dwóch łańcuchów
tekstowych warto dla czytelności dodać spację, by nie uzyskiwać
napisów typu: WaldekKowalski. Łatwo można przegapić i inne
ograniczniki (ang. delimiter).
* Ocena długości tekstu.
Szczególnie przewidujący i ostrożny musi być programista wtedy,
gdy łańcuch będzie wprowadzany przez użytkownika programu.
________________________________________________________________
Wyszukiwarka
Podobne podstrony:
www livemocha com angielski lekcja audio
jezyk ukrainski lekcja 03
Lekcja sortowanie
lekcja12
Kris Jamsa Wygraj Z C lekcja32
lekcja1 (2)
Lekcja7
ćw oswajające z piłką lekcja dla dzieci
Logo na lekcjach matematyki w szkole podstawowej
lekcja
C LEKCJA23
Kris Jamsa Wygraj Z C lekcja 5
Lekcja algorytmy w geometrii
LEKCJA 1 Uwierz w siebie, możesz wszystko!
Lekcja 7 Trening pamieci to nie wszystko Zadbaj o swoja koncentracje
lekcja6
więcej podobnych podstron