C++ LEKCJA18


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