9 Napisy. Napisy w stylu języka C

Podobne dokumenty
Ćwiczenie nr 6. Poprawne deklaracje takich zmiennych tekstowych mogą wyglądać tak:

Języki programowania obiektowego Nieobiektowe elementy języka C++

Podstawy Programowania

Podstawy programowania skrót z wykładów:

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 26 marca kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 40

Podstawy programowania w języku C++

Języki programowania. Przetwarzanie tablic znaków. Część druga. Autorzy Tomasz Xięski Roman Simiński

Podstawy programowania

DANE TEKSTOWE W JĘZYKU C/C++ - TABLICE ZNAKOWE

Katedra Elektrotechniki Teoretycznej i Informatyki. wykład 9 - sem.iii. Dr inż. M. Czyżak

Część 4 życie programu

Inicjacja tablicy jednowymiarowej

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Zasady programowania Dokumentacja

dr inż. Jarosław Forenc

Zmienne i struktury dynamiczne

Programowanie i struktury danych

Programowanie komputerowe. Zajęcia 4

Laboratorium 6: Ciągi znaków. mgr inż. Leszek Ciopiński dr inż. Arkadiusz Chrobot dr inż. Grzegorz Łukawski

Informacje wstępne #include <nazwa> - derektywa procesora umożliwiająca włączenie do programu pliku o podanej nazwie. Typy danych: char, signed char

Wykład II. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Języki i metodyka programowania. Wprowadzenie do języka C

iii. b. Deklaracja zmiennej znakowej poprzez podanie znaku

Wskaźniki. nie są konieczne, ale dają językowi siłę i elastyczność są języki w których nie używa się wskaźników typ wskaźnikowy typ pochodny:

Podstawy języka C++ Maciej Trzebiński. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. Praktyki studenckie na LHC IVedycja,2016r.

wykład II uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C - funkcje, tablice i wskaźniki wykład II dr Jarosław Mederski Spis

Wskaźniki. Informatyka

Katedra Elektrotechniki Teoretycznej i Informatyki. wykład 7- sem.iii. M. Czyżak

1. Pierwszy program. Kompilator ignoruje komentarze; zadaniem komentarza jest bowiem wyjaśnienie programu człowiekowi.

Uwagi dotyczące notacji kodu! Moduły. Struktura modułu. Procedury. Opcje modułu (niektóre)

Tablice, funkcje - wprowadzenie

Wymiar musi być wyrażeniem stałym typu całkowitego, tzn. takim, które może obliczyć kompilator. Przykłady:

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk. Wydział Inżynierii Metali i Informatyki Przemysłowej

ZASADY PROGRAMOWANIA KOMPUTERÓW

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Programowanie Proceduralne

Wstęp do programowania

Tablice (jedno i wielowymiarowe), łańcuchy znaków

Struktura pliku projektu Console Application

Wstęp do programowania

Wstęp do programowania

Wstęp do programowania

// Potrzebne do memset oraz memcpy, czyli kopiowania bloków

Pliki w C/C++ Przykłady na podstawie materiałów dr T. Jeleniewskiego

Programowanie komputerowe. Zajęcia 5

ŁAŃCUCHY W JĘZYKU C/C++

Podstawy programowania. Wykład: 9. Łańcuchy znaków. dr Artur Bartoszewski -Podstawy programowania, sem 1 - WYKŁAD

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

ZASADY PROGRAMOWANIA KOMPUTERÓW ZAP zima 2015

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Programowanie i struktury danych

I - Microsoft Visual Studio C++

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Język C++ wykład VIII

DYNAMICZNE PRZYDZIELANIE PAMIECI

Podstawy języka C++ Maciej Trzebiński. Praktyki studenckie na LHC IFJ PAN. Instytut Fizyki Jądrowej Polskiej Akademii Nauk. M. Trzebiński C++ 1/16

Programowanie C++ Wykład 2 - podstawy języka C++ dr inż. Jakub Możaryn. Warszawa, Instytut Automatyki i Robotyki

Dla każdej operacji łącznie tworzenia danych i zapisu ich do pliku przeprowadzić pomiar czasu wykonania polecenia. Wyniki przedstawić w tabelce.

Wykład I. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Programowanie w C++ Wykład 5. Katarzyna Grzelak. 16 kwietnia K.Grzelak (Wykład 1) Programowanie w C++ 1 / 27

Laboratorium 3: Tablice, tablice znaków i funkcje operujące na ciągach znaków. dr inż. Arkadiusz Chrobot dr inż. Grzegorz Łukawski

1 Wskaźniki i zmienne dynamiczne, instrukcja przed zajęciami

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

1 P roste e t ypy p d a d n a ych c - c ąg ą g d a d l a szy 2 T y T py p z ł z o ł żo ż ne e d a d n a ych c : T BLICE

// Liczy srednie w wierszach i kolumnach tablicy "dwuwymiarowej" // Elementy tablicy są generowane losowo #include <stdio.h> #include <stdlib.

Tablice wielowymiarowe. Przykład tablica 2-wymiarowa. Przykład. Przykład 3-wymiarowy. Tak naprawdę nie istnieją w C! Rozważmy tablicę o rozmiarze 3x2

Podstawy programowania 1

Rozwiązanie. #include <cstdlib> #include <iostream> using namespace std;

Programowanie komputerowe. Zajęcia 1

Tablice deklaracja, reprezentacja wewnętrzna

Podstawy Informatyki. Inżynieria Ciepła, I rok. Wykład 10 Kurs C++

Wstęp do wskaźników w języku ANSI C

Wstęp do programowania. Wykład 1

main( ) main( void ) main( int argc, char argv[ ] ) int MAX ( int liczba_1, liczba_2, liczba_3 ) źle!

Języki programowania. Przetwarzanie plików amorficznych Konwencja języka C. Część siódma. Autorzy Tomasz Xięski Roman Simiński

Ćwiczenie 7 z Podstaw programowania. Język C++, programy pisane w nieobiektowym stylu programowania. Zofia Kruczkiewicz

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Argumenty wywołania programu, operacje na plikach

Pliki wykład 2. Dorota Pylak

Materiał uzupełniający do ćwiczen z przedmiotu: Programowanie w C ++ - ćwiczenia na wskaźnikach

Podstawy Programowania Podstawowa składnia języka C++

tablica: dane_liczbowe

Algorytmy i język C++

Pobieranie argumentów wiersza polecenia

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

Stałe i zmienne znakowe. Stała znakowa: znak

Programowanie w językach

Lab 9 Podstawy Programowania

PARADYGMATY PROGRAMOWANIA Wykład 3

//zmienne globalne int *pa, *pb; //wskaźniki globalne void main(void) { clrscr(); printf("\n podaj wartosc liczby a\n"); scanf("%d",&a); pa=&a;

Zajęcia nr 2 Programowanie strukturalne. dr inż. Łukasz Graczykowski mgr inż. Leszek Kosarzewski Wydział Fizyki Politechniki Warszawskiej

Wprowadzenie do programowania i programowanie obiektowe

Programowanie Obiektowo Zorientowane w języku c++ Przestrzenie nazw

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 21 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 24

> C++ wskaźniki. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki 26 kwietnia 2017

lekcja 8a Gry komputerowe MasterMind

Wstęp do programowania

Wstęp do programowania

Spis treści WSTĘP CZĘŚĆ I. PASCAL WPROWADZENIE DO PROGRAMOWANIA STRUKTURALNEGO. Rozdział 1. Wybór i instalacja kompilatora języka Pascal

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 5

Transkrypt:

9 Napisy Napis (inne nazwy to tekst, łańcuch, ang. string) jest to grupa znaków traktowanych jako całość. Napis może zawierać litery, cyfry, znaki specjalne. Języki C/C++ nie posiadają wbudowanego podstawowego typu pozwalającego przechowywać napisy. W języku C napis (ang. string) jest przechowywany jako ciąg znaków zakończonych znakiem pustym (znak o kodzie ASCII 0, '\0'). W języku C++ można korzystać z reprezentacji napisu: o jako ciągu znaków zakończonych znakiem pustym, mówimy wtedy o napisie w stylu języka C, o jako obiektu klasy string, należącego do standardu języka C++. Napisy w stylu języka C Napis jest przechowywany w tablicy typu char. Napis kończy się znakiem pustym '\0' (znak o kodzie ASCII 0 - NULL). Dzięki temu standardowe funkcje wiedzą, gdzie kończy się napis bez konieczności podawania jego długości. Przykład: Przechowywanie znaku w pamięci zmienna typu char znak adres 2000 zawartość 'x' Przechowywanie napisu w pamięci tablica typu char element t[0] t[1] t[2] t[3] t[4] t[5] adres 1000 1001 1002 1003 1004 1005 zawartość 'w' 'i' 't' 'a' 'm' '\0' 1

9.1 Deklarowanie i inicjowanie napisów Do przechowywania napisu należy zadeklarować tablicę typu char. Przykład deklaracji: char napis[5]; // pozwala przechować do 4 znaków napisu i znak '\0' Podczas deklaracji można pominąć rozmiar tablicy: char imie[5]="ania"; // rozmiar tablicy zawsze o 1 więcej niż długość tekstu char imie[]="ania"; // kompilator sam dobierze rozmiar tablicy 'a' 'n' 'i' 'a' '\0' Kompilator automatycznie uzupełniani stałą tekstową (ciąg znaków w cudzysłowach " " ) ogranicznikiem tekstu '\0' (symbol NULL). 9.2 Wczytywanie i wyświetlanie napisów Metoda 1 - za pomocą operatorów << i >> #include <iostream> //p9.1 int main() char nazwisko[20]; cout << "Podaj nazwisko: "; cin >> nazwisko; cout << "Nazywasz sie " << nazwisko << endl; Wykonanie: Podaj nazwisko: Kowalski Nazywasz się Kowalski Podaj nazwisko: Jan Kowalski Nazywasz się Jan // czyta do spacji Komentarz: Konstrukcji cin >> nazwisko, gdzie nazwisko jest tablicą znakową określonej długości należy unikać, ponieważ łatwo można przekroczyć rozmiar tablicy przeznaczonej na wprowadzane dane. Wystarczy wpisać dłuższy napis niż zadeklarowana na jego przyjęcie tablica, przekroczenie rozmiaru nie jest bowiem sprawdzane. Metoda 2 - za pomocą funkcji getline() #include <iostream> //p9.2 #include <cstring> int main() char nazwisko[20]; cout << "Podaj imie i nazwisko: "; cin.getline(nazwisko,20); //bezpieczny sposób cout << "Nazywasz sie " << nazwisko << endl; int ile=cin.gcount(); cout << "Wczytano: " << ile << " znakow" << endl; int dlugosc=strlen(nazwisko); cout << "Dlugosc wczytanego tekstu: " << dlugosc << " znaków" << endl; 2

Wykonanie: Podaj imie i nazwisko: Jan Kowalski Nazywasz sie: Jan Kowalski Wczytano 13 znakow Długość wczytanego tekstu: 12 Uwagi: Funkcja getline wczytuje napis do bufora podanego jako pierwszy parametr. Wczytywanie domyślnie jest wykonywane do napotkania znaku nowej linii '\n'. Dzięki temu można wczytać cały wiersz tekstu, również spacje rozdzielające słowa. (Uwaga: domyślny znak końca można zmienić, patrz opis funkcji). Funkcja zabezpiecza się przed przekroczeniem rozmiaru bufora, ponieważ odczyt jest przerywany po wczytaniu n-1 znaków, gdzie n jest drugim parametrem funkcji getline(). Znak końca wiersza nie jest umieszczany w buforze podanym jako parametr. Jednakże jest on wliczany do przeczytanych znaków, stąd uwzględnia go funkcja gcount(). Po ostatnim wczytanym znaku funkcja getline umieszcza znak końca napisu '\0'. 3

9.3 Związek między tablicą znakową a wskaźnikiem Tak jak w zwykłej tablicy - nazwa tablicy jest adresem pierwszego elementu tablicy. Do elementów tablicy można się odwoływać za pomocą indeksów lub za pomocą operatora wyłuskania *. Przykład 1. Zależność między tablicą i wskaźnikiem Dla tablicy char imie[]="ania"; prawdziwa jest zależność: imie[0] == *imie == 'a' Przykład 2. Różnica między tablicą i wskaźnikiem W deklaracji napis może być przypisany zarówno tablicy znaków jak i zmiennej wskaźnikowej typu char*. Przypisanie napisu tablicy: char k[]="szary"; element k[0] k[1] k[2] k[3] k[4] k[5] adres 1000 1001 1002 1003 1004 1005 zawartość 's' 'z' 'a' 'r' 'y' '\0' Działanie: Tworzona jest tablica, w każdym elemencie tej tablicy przechowywany jest kolejny znak, na końcu dopisywany jest symbol końca napisu \0. Przypisanie napisu wskaźnikowi: char *wsk_k="szary"; nazwa wsk_k adres 2000 zawartość 1010 adres 1010 1011 1012 1013 1014 1015 zawartość 's' 'z' 'a' 'r' 'y' '\0' Działanie: Tworzona jest zmienna wskaźnikowa, w niej umieszczany jest adres obszaru pamięci, w którym zostanie umieszczony napis uzupełniony symbolem końca napisu \0. Podobieństwa i różnice o W obu przypadkach można korzystać z notacji tablicowej: for (i=0; k[i]!= '\0';i++) cout << k[i]; cout << endl; for (i=0; wsk[i]!=0; i++) cout << wsk[i]; cout << endl; 4

o W obu przypadkach można korzystać z notacji wskaźnikowej: for (i=0; *(k+i)!= '\0';i++) cout << *(k+i); cout << endl; for (i=0; *(wsk+i)!= '\0';i++) cout << *(wsk_k+i)); cout << endl; o Tylko w notacji wskaźnikowej można zmieniać wartość zmiennej wskaźnikowej: nie while(*(wsk)!= '\0') cout << *wsk_k++; cout << endl; Przykłady przypisywania napisów char kolor[]="zielony"; //OK: // podczas inicjacji można przypisać tablicy wartość char *wsk="zielony"; // OK: // podczas inicjacji można przypisać wskaźnikowi wartość char kolor[10], *wsk1; kolor="zielony"; // błąd: // nazwa tablicy jest wskaźnikiem stałym // przypisanie wartości tylko podczas inicjacji wsk1="zielony"; // OK: // wskaźnik jest zmienną; // teraz wsk1 wskazuje na nowy napis gdzieś w pamięci char kolor[10]; char *wsk1; kolor[6] = 'a'; // OK: // przypisanie wartości elementowi tablicy wsk1[6] = 'e'; // błąd: // wskaźnik musi być zainicjowany char *wsk="zielony"; wsk1[6] = 'e'; // błąd: // napis jest stały char kolor[]="zielony"; char *wsk1; wsk1=kolor; wsk1[6] = 'e'; // OK: // wsk1 można zmieniać char *wsk2; *wsk2 = "niebieski"; // błąd: // adres może być przypisany tylko wskaźnikowi wsk2 = "niebieski"; // OK: // w tym przypadku wskaźnik może nie być zainicjowany 5

Przykłady różne Wyświetlanie napisu od końca #include <iostream> //p9.3 #include <cstring> int main() char napis[80], roboczy; int pierwszy,ostatni; cout << "Wpisz tekst:" << endl; cin.getline(napis,80); pierwszy=0; ostatni=strlen(napis)-1; while (ostatni>pierwszy) roboczy=napis[pierwszy]; napis[pierwszy++] = napis[ostatni]; napis[ostatni--]=roboczy; #include <iostream> //p9.4 #include <cstring> int main() char napis[80], roboczy; char *pierwszy,*ostatni; cout << "Wpisz tekst: "; cin.getline(napis,80); pierwszy=napis; ostatni=napis+(strlen(napis)-1); while (ostatni>pierwszy) roboczy=*pierwszy; *(pierwszy++) = *ostatni; *(ostatni--)=roboczy; cout << "Napis od tylu:" << endl << napis << endl; cout << "Napis od tylu:" << endl << napis << endl; 6

9.4 Przekazywanie napisów do funkcji Przykład 1 #include <iostream> void drukuj(char tekst[]) cout << tekst << endl; int main() drukuj("adam Nowak"); drukuj("piotr Janicki"); #include <iostream> void drukuj(char *tekst) cout << tekst << endl; int main() drukuj("adam Nowak"); drukuj("piotr Janicki"); Przykład 2. Obliczanie długości napisu (czyli różne alternatywy dla funkcji bibliotecznej strlen() ) //p9.5 include iostream> int dlugosc_napisu(char *napis) int dlugosc=0; while (*napis!= '\0') dlugosc++; napis++; return (dlugosc); int dlugosc_napisu(char *napis) int dlugosc; if (napis) for(dlugosc=0; *(napis++)!='\0';) dlugosc++; return (dlugosc); int dlugosc_napisu(char *napis) int dlugosc=0; if (napis) while (*(napis++)) dlugosc++; return (dlugosc); int dlugosc_napisu(char *napis) if (!napis) for (char wsk=napis; wsk; ++wsk) return (wsk-napis); int main() char nazwisko[]="nowak"; cout << "Nazwisko " << nazwisko << " ma " << dlugosc_napisu(nazwisko) << " znaków " << endl; 7

Przykład 3 #include <iostream> char *wielkie_litery(char *tekst) char *poczatek=tekst; // adres tekst[0] while (*tekst) if ( (*tekst>='a') && (*tekst<='z')) *tekst = *tekst - 'a' +'A'; tekst++ ; return (poczatek); int main () char tekst[]="jan Nowak"; cout << wielkie_litery(tekst) << endl; Przykład 4. #include <iostream> //p9.7 /* Funkcja dopasuj() zwraca wskaźnik do miejsca napisu, w którym znajduje się pierwsze wystąpienie znaku c. Jeżeli znak taki nie istnieje, to zwracany jest wskaźnik do znaku końca napisu */ char *dopasuj(char c, char *s); int main() char s[80], *p, znak; cout << "Wpisz tekst:"<< endl; cin.getline(s,80); cout << "Wpisz znak:"<<endl; cin >> znak; p=dopasuj(znak,s); if (*p) /* znak został znaleziony */ cout << "Tekst od znaku " << endl << p << endl; else cout << "Znak nie został znaleziony." << endl; char *dopasuj(char c, char *s) int licznik=0; while (c!=s[licznik] && s[licznik]) licznik++; return (&s[licznik]); Inne wersje funkcji dopasuj() char *dopasuj2(char c, char *s) while (*s && *s!=c) ++s; return (s); char *dopasuj3(char c, char *s) char *Wynik=strchr(s,c); if(wynik) return(wynik); return (s+strlen(s)); Komentarz: char *strchr(char*s, int c)jest funkcją biblioteczną. Znajduje pierwsze wystąpienie znaku c w napisie s. Zwraca wskaźnik odnalezionego znaku lub NULL, jeśli znak nie występuje w napisie. 8

9.5 Funkcje tekstowe Skrót K&R oznacza przykład z książki Kernighan & Ritchie, Język ANSI C, Warszawa 2003 Komentarz: wiele z tych funkcji można napisać bardziej zwięźle, korzystając z właściwości języka C, w szczególności z faktu, że wartość 0 oznacza FALSE zaś każda wartość 0 wartość TRUE. Przytoczono je w postaci oryginalnej, ponieważ wyraźniej widać wtedy sposób przetwarzania napisu. Obliczanie długości napisu Długość napisu to liczba zawartych w nim znaków, bez znaku końca napisu. /* K&R str. 138: oblicz długość napisu int strlen(char *s) int n; for (n=0; *s!= '\0'; s++) n++; return n; /* K&R str. 142: oblicz długość napisu int strlen(char *s) char *p=s; while (*p!= '\0') p++; return p-s; Kopiowanie napisów /* K&R str. 145: skopiuj t do s */ void strcpy(char *s, char *t) int i; i=0; while ((s[i]=t[i])!= '\0') i++; /* K&R str. 145: skopiuj t do s */ void strcpy(char *s, char *t) while ((*s=*t)!= '\0') s++; t++; /* K&R str. 145: skopiuj t do s */ void strcpy(char *s, char *t) while ((*s++=*t++)!= '\0') ; /* K&R str. 146: skopiuj t do s */ void strcpy(char *s, char *t) while (*s++=*t++) ; 9

Łączenie napisów /* K&R str.76: połącz dwa napisy */ void strcat(char s[],char t[]) int i,j; i=j=0; while (s[i]!= '\0') /* znajdź koniec tekstu w s */ i++; while((s[i++]=t[j++])!= '\0') /* przepisz tekst */ ; Porównywanie napisów /* K&R str. 146: porównaj dwa napisy zwróć: <0 jeśli s<t, 0jeśli s==t, >0 jeśli s>t zwracaną wartość otrzymuje się odejmując od siebie znaki; pierwszej pozycji, na której teksty się różnią */ int strcmp(char *s, char *t) int i; for (i=0;s[i]==t[i];i++) if (s[i]=='\0') return s[i]-t[i]; /* K&R str. 147: porównaj dwa napisy zwróć: <0 jeśli s<t, 0jeśli s==t, >0 jeśli s>t */ int strcmp(char *s, char *t) for (; *s==*t;s++,t++) if (*s=='\0') return *s-*t; Działania na znakach /* K&R str. 75: usuń z tekstu znak c */ void squeeze(char s[],int c) int i,j; for(i=j=0;s[i]!='\0';i++) if (s[i]!= c) s[j++]=s[i]; s[j]='\0'; /* K&R str. 93: odwróć tekst w miejscu */ void reverse(char s[]) int c,i,j; for (i=0,j=strlen(s)-1; i<j; i++,j--) c=s[i]; s[i]=s[j]; s[j]=c; 10

Konwersje /* K&R str. 69 zamień s na wartość całkowitą */ int atoi(char s[]) int i,n; n=0; for (i=0;s[i]>='0' && s[i]<='9';++i) n=10*n+(s[i]-'0'); return n; 11

9.6 Funkcje biblioteczne działające na napisach Funkcje działające na napisach korzystają z pliku nagłówkowego cstring (string.h w starszych wersjach). Prototyp funkcji Opis funkcji size_t strlen(const char *s1) char *strcpy(char *s1, const char *s2) Zwraca długość napisu wskazywanego przez s1 (bez znaku kończącego napis '\0'). Kopiuje napis z s2 do tablicy znakowej s1. Zwraca s1. Uwaga: tablica znakowa s1 musi mieć wystarczającą długość, aby zmieścił się w niej napis s2. char *strncpy(char *s1, const char *s2, size_t n) Kopiuje z s2 co najwyżej n znaków do tablicy znakowej s1. Zwraca s1. Uwaga: znak kończący napis s2 zostanie skopiowany tylko wtedy, kiedy n jest większe od długości s2, w przeciwnym wypadku trzeba samemu dopisać znak '\0'. Tablica znakowa s1 musi mieć wystarczającą długość, aby zmieściło się w niej n znaków napisu s2 i znak końca napisu. char *strcat(char *s1, const char *s2) Dołącza napis s2 do napisu s1. Pierwszy znak s2 nadpisuje znak końca s1. Zwraca s1. Uwaga: tablica znakowa s1 musi mieć wystarczającą długość, aby zmieścił się w niej połączony napis. char *strncat(char *s1, const char *s2, size_t n) Dołącza co najwyżej n znaków napisu s2 do napisu s1. Na końcu utworzonego napisu umieszcza '\0'. Zwraca s1. Uwaga: tablica znakowa s1 musi mieć wystarczającą długość, aby zmieścił się w niej połączony napis. int strcmp(const char *s1, const char *s2) Porównuje napis s1 z napisem s2 i zwraca: jeżeli oba napisy są równe liczbę > 0, jeżeli napis s1 jest większy od napisu s2 liczbę < 0, jeżeli napis s1 jest mniejszy od napisu s2. Porównywanie jest wykonywane leksykograficznie. int strncmp(const char *s1, const char *s2, size_t n) Porównuje leksykograficznie n znaków napisu s1 z napisem s2 i zwraca: jeżeli oba napisy są równe liczbę > 0, jeżeli napis s1 jest większy od napisu s2 liczbę < 0, jeżeli napis s1 jest mniejszy od napisu s2. Porównywanie jest wykonywane leksykograficznie. char *strchr(char*s, int c) Znajduje pierwsze wystąpienie znaku c w napisie s. Zwraca wskaźnik odnalezionego znaku lub NULL, jeśli znak nie występuje w napisie. char *strrchr(char*s, int c) Znajduje ostatnie wystąpienie znaku c w napisie s. Zwraca wskaźnik odnalezionego znaku lub NULL, jeśli znak nie występuje w napisie. 12

char *strstr(const char *s1, const char *s2); Sprawdza czy napis s1 zawiera napis s2; zwraca wskaźnik do pierwszego wystąpienia napisu s2 w tekście s1, lub NULL, jeśli s2 nie występuje w s1 Przykład 1. #include <iostream> #include <string> int main() char x[]="adam Kowalski"; char y[50], z[25]; cout << "Napis w tablicy x:"<< endl << x <<endl; strcpy(y,x); // kopiowanie do tablicy y cout << "Napis w tablicy y:"<< endl << y <<endl; strncpy(z,x,4); // nie kopiuje znaku końca napisu z[4]='\0'; // trzeba samemu dopisać znak końca cout << "Napis w tablicy z:"<< endl << z <<endl; Przykład 2. #include <iostream> #include <cstring> int main() char napis[80]; char *p_wsk, *k_wsk; char znak; cout << "Wpisz tekst:"<<endl; cin.getline(napis,80); p_wsk=napis; k_wsk=p_wsk+(strlen(napis)-1); while (k_wsk > p_wsk) znak= *p_wsk; *p_wsk++ = *k_wsk; *k_wsk-- = znak; cout << "Odwrocony tekst:" << endl << napis << endl; Przykład 3 #include <iostream> #include <cstring> int main() int i; char alfabet[27], dopisz[2]; dopisz[1]='\0'; strcpy(alfabet,"a"); for (i='b';i<='z';i++) dopisz[0]=i; strcat(alfabet,dopisz); 13

cout << "Alfabet" << endl << alfabet<<endl; 9.7 Wyszukiwanie Podstawowe funkcje pozwalające przeszukiwać napisy to: char *strchr(char*s, int c) szuka pierwszego wystąpienia znaku c w napisie s. char *strrchr(char*s, int c) - szuka ostatniego wystąpienia znaku c w napisie s. char *strstr(const char *s1, const char *s2) szuka napisu s2 w s1 char *strtok(char *s1, const char *s2) wyszukuje w napisie s1 ciągi znaków rozdzielone znakami separatorów zdefiniowanych w napisie s2 Korzystając z tych funkcji można budować inne, bardziej złożone. Przykład: znaleźć ostatnie wystąpienie napisu s2 w s1; funkcja ma zwracać wskaźnik do miejsca, w którym rozpoczyna się ostatnie wystąpienie napisu s2, jeśli napis s1 nie zawiera napisu s2, funkcja ma zwrócić NULL char *strrstr( char *s1, char *s2 ) char *ostatni = NULL; char *biezacy; // szukaj tylko wtedy, kiedy napis s2 nie jest pusty if( *s2!= '\0' ) // znajdź pierwsze wystąpienie napisu biezacy = strstr( s1, s2 ); // powtarzaj wyszukiwanie while( biezacy!= NULL ) ostatni = biezacy; biezacy = strstr( ostatni + 1, s2 ); return ostatni; Przykład: podzielić tekst na wyrazy, każdy z nich wydrukować w oddzielnej linii; separatorem wyrazu jest spacja, tabulacja, powrót karetki, znak nowego wiersza. Wykorzystać funkcję biblioteczną strtok(). void drukuj_wyrazy( char *tekst ) // separatory: spacja, tabulacja, powrót karetki, nowy wiersz static char odstep[] = " \t\r\n"; char *wyraz; for( wyraz = strtok( tekst, odstep ); wyraz!= NULL; wyraz = strtok( NULL, odstep ) ) cout << "Nastepny wyraz: "<< wyraz << endl ; Komentarz: Funkcja strtok() działa następująco: jeśli pierwszy argument jest różny od NULL, funkcja znajduje pierwszy wyraz (ang.token) oddzielony od reszty tekstu jednym z podanych separatorów w drugim argumencie, zapamiętuje jego pozycję i zwraca wskaźnik do początku znalezionego wyrazu. Jeśli pierwszy argument jest równy NULL, funkcja korzystając z zapamiętanej pozycji poprzedniego wyrazu, zwraca wyraz następny i zapamiętuje jego pozycję. Funkcja zwróci NULL, gdy nie znajdzie więcej wyrazów. 14

Przykład: Napisać własną funkcję, która znajduje pierwsze wystąpienie wyrazu w podanym tekście. Sformułowanie zadania: Dany jest ciąg znaków (tekst) o długości n. Szukamy w nim ciągu znaków (wzorzec) o długości m. Tego typu problemy nazywane są problemami wyszukiwania wzorca (ang. pattern matching). Najprostszym algorytmem rozwiązującym to zadanie jest algorytm naiwny (ang. straightforward searching). Badamy każdą pozycję k tekstu i sprawdzamy, czy kolejne m znaków jest zgodnych ze wzorcem. Przykład: szukamy w tekście słowa lekarz // wersja A - tablice int SzukajA(char *tekst, char *wzorzec) // Zwraca pozycję, od której rozpoczyna się wzorzec, // jeżeli wzorca nie znaleziono zwracane jest -1 int t_dl=strlen(tekst); int w_dl=strlen(wzorzec); for (int poz=0; poz<t_dl; poz++) // poz indeks przeglądanego tekstu int w=0; // w indeks wzorca for (int t = poz; w<w_dl; w++, t++) // t indeks przeglądanego tekstu // w kolejnym sprawdzeniu if (wzorzec[w]!= tekst[t]) break; if (w == w_dl) return poz; // Wzorzec rozpoczyna się na pozycji poz return -1; Komentarz: wersja A jest najprostsza, każdy znak w tekście może być początkiem wzorca. 15

// wersja B zastąpienie tablicy wskaźnikami int SzukajB(char *tekst, char *wzorzec) char *t=tekst, *koniec_t=tekst+strlen(tekst); char *koniec_w=wzorzec+strlen(wzorzec); while(t < koniec_t) char *w=wzorzec; char *q = t; while(w < koniec_w && *++w == *++q) if (w == koniec_w) return t - tekst; t++; return -1; Komentarz: wersja B zastosowanie wskaźników, tablica jest przeglądana sekwencyjnie, zwiększymy dzięki temu szybkość. // wersja C nie każdy znak w tekście może być początkiem wzorca int SzukajC(char *tekst, char *wzorzec) int w_dl=strlen(wzorzec); char *t=tekst, *koniec_t=tekst+strlen(tekst)-w_dl+1; char *koniec_w=wzorzec+w_dl; char pierwszy = *wzorzec; while(true) // Szukamy pierwszego znaku wzorca while (t < koniec_t && *t!= pierwszy) t++; if (t >= koniec_t) break; // Znaleziono pierwszy znak wzorca, rozpocznij sprawdzanie char *w = wzorzec; char *q = t; while(w < koniec_w && *++w == *++q) if (w == koniec_w) return t - tekst; t++; return -1; Komentarz: W poprzednich wersjach przyjęto, że każdy znak w tekście może być początkiem wzorca, mimo że pewne części tekstu można by było przeskoczyć, gdyż wiadomo, że nie będą one odpowiadały wzorcowi. W wersji C przyjęto, że algorytm można podzielić na dwie części: poszukiwanie w tekście pierwszego znaku, od którego rozpoczyna się wzorzec dopiero wtedy rozpoczęcie dokładnego sprawdzania Dodatkowo uwzględniono fakt, że jeżeli w tekście pozostanie mniej znaków do sprawdzania, niż ma ich wzorzec, nie ma sensu kontynuowanie sprawdzania (koniec tekstu koniec_t=strlen(tekst)-w_dl+1) // Wersja D - uzycie funkcji bibliotecznych int SzukajD(char *tekst,char *wzorzec) char poczatek=*wzorzec; int w_dl=strlen(wzorzec); char *kt=tekst+strlen(tekst); for(char *t=strchr(tekst,poczatek); t && t<kt; t=strchr(t+1,poczatek)) if(!memcmp(t+1,wzorzec+1,w_dl-1)) return(t-tekst); return(-1); 16

9.8 Tablice napisów Tablica napisów pozwala korzystać z różnych napisów wskazując je za pomocą indeksu. Tablica wskaźników do napisów Napisy można przechowywać w tablicy dwuwymiarowej char komunikat[3][10] ="Witamy", "Welcome", "Bienvenue"; Można również przechowywać napisy w postaci tablic jednowymiarowych, zaś dostęp do nich zapewnić za pomocą wskaźnika przechowywanego w tablicy wskaźników. char *komunikat[3]="witamy", "Welcome", "Bienvenue"; Dostęp do napisu : cout << komunikat[1]; Przykład: void blad(int numer) static char *komunikat[]= "Blad otwarcia pliku\n", "Blad odczytu\n", "Blad zapisu\n" ; cout << komunikat[numer]; 17

Przykład wykorzystania tablicy wskaźników: program wczytuje nazwiska do bufora umieszczając je jedno za drugim. #include <iostream> #include <cstring> const int MAX_WSK = 5; const int MAX_DANE = 100; int main() char *tablica_wsk[max_wsk], dane[max_dane]; int pozycja=0, i; for (i=0; i<max_wsk; i++) cout << "Podaj nazwisko nr " << (i+1) <<":"; cin.getline(dane+pozycja,max_dane-pozycja); tablica_wsk[i]=dane+pozycja; pozycja += strlen(tablica_wsk[i])+1; cout << "Wczytane nazwiska:" << endl; for (i=0; i<max_wsk; i++) cout << "Nazwisko nr " << (i+1) << ":" << tablica_wsk[i] << endl; Pytanie: czy program jest zabezpieczony przed przekroczeniem rozmiaru tablicy zarezerwowanej na dane? 18

9.9 Przekazywanie tablicy wskaźników do funkcji Przykład 1: program wczytuje 5 nazwisk zmiennej długości, umieszcza je w tablicy jeden za drugim, następnie je sortuje metodą sortowania bąbelkowego. Funkcja sortowanie() jako pierwszy argument otrzymuje tablicę wskaźników do początków nazwisk. Drugim argumentem jest liczba elementów w tablicy czyli liczba napisów do sortowania. Prototyp funkcji sortowanie() : void sortowanie(char *napis[], int ile) lub void sortowanie(char **napis, int); #include <iostream> //p9.12 #include <string.h> void sortowanie(char**, int); const int MAX_TAB = 5; const int MAX_DL = 50; int main() char *wsk_nazwiska[max_tab], tbl_nazwiska[max_dl]; int indeks_rob=0; int i; for (i=0; i<max_tab; i++) cout << "Podaj nazwisko :"; cin.getline(tbl_nazwiska+indeks_rob,max_dl-indeks_rob); wsk_nazwiska[i]=tbl_nazwiska+indeks_rob; indeks_rob += strlen(wsk_nazwiska[i])+1; cout << "Przed sortowaniem: "<<endl; for (i=0; i<max_tab; i++) cout << "Nazwisko: " << wsk_nazwiska[i] << endl; sortowanie(wsk_nazwiska, MAX_TAB); cout << "Po sortowaniu: "<<endl; for (i=0; i<max_tab; i++) cout << "Nazwisko: " << wsk_nazwiska[i] << endl; void sortowanie(char *napis[], int ile) char *robocza; int i,j; for (i=0; i< ile-1; i++) for (j=(ile-1); i<j; j--) if (strcmp(napis[j-1],napis[j]) > 0) robocza=napis[j-1]; napis[j-1]=napis[j]; napis[j]=robocza; 19

Przykład 2: funkcja sprawdza, czy w tablicy znajduje się podane słowo i zwraca indeks do tego elementu tablicy, jeśli słowo nie zostało znalezione zwraca -1. Wersja A: funkcja ma przetwarzać tablicę postaci: char const *tablica[]= "warszawa", "krakow", "gniezno", "poznan" ; int szukaj_slowa( char const *tablica[], int rozmiar, char const * const slowo ) char const **wt; // zmienna robocza for( wt = tablica; wt < tablica + rozmiar; wt++ ) // jeśli słowo zgadza się z elementem tablicy, // zwróć indeks elementu if( strcmp( slowo, *wt ) == 0 ) return wt - tablica; // nie znaleziono return -1; Wersja B: funkcja ma przetwarzać tablicę postaci: char const *tablica[]= "warszawa", "krakow", "gniezno", "poznan", NULL ; Koniec tablicy jest oznaczony za pomocą wskaźnika pustego. Pozwoli on znaleźć koniec tablicy i nie trzeba do funkcji przesyłać rozmiaru tablicy. int szukaj_slowa( char const *tablica[], char const * const slowo ) char const **wt; // zmienna robocza for( wt = tablica; *wt!= NULL ; wt++ ) // jeśli słowo zgadza się z elementem tablicy, // zwróć indeks elementu if( strcmp( slowo, *wt ) == 0 ) return wt - tablica; // nie znaleziono return -1; 20

9.10 Przykład: argumenty funkcji main() Standard ANSI definiuje dwa parametry, które można przekazywać funkcji main(): argc i argv. Pozwalają one przekazywać programom C++/C argumenty z wiersza wywołania: nazwa programu test plik1 plik2 plik3 argumenty przekazywane do programu Aby mieć dostęp do argumentów przekazywanych w wierszu wywołania program, nagłówek funkcji main() musi mieć postać: int main(int argc, char *argv[]) o o Parametr argc zawiera liczbę argumentów (ang. argument count) przekazywanych do programu. Jego wartość wynosi co najmniej 1, ponieważ nazwa programu jest również traktowana jako argument. Parametr argv (ang. argument value) to wskaźnik do tablicy wskaźników odsyłających do napisów będących argumentami z wiersza wywołania. Przykład: programu drukuje argumenty przekazane w wierszu wywołania #include <iostream> int main(int argc, char *argv[]) cout << "Wartosc argc to: " << argc << endl; cout << "Wartosci argumentow wiersza polecenia: " << endl; for (int i = 0; i < argc; i++) cout << " argv[" << i << "]: " << argv[i] << endl; Wywołanie programu:./test plik1 plik2 plik3 Wykonanie programu: Wartosc argc to: 4 Wartosc argumentow wiersza polecenia: argv[0]:./test argv[1]: plik1 argv[2]: plik2 argv[3]: plik3 Można również wykorzystać fakt, że tablica z argumentami jest zakończona wskaźnikiem pustym: #include <iostream> int main(int argc, char *argv[]) cout << "Wartosci argumentow wiersza polecenia: " << endl; while (*++argv!= NULL) cout << *argv << endl; 21

9.11 Przykłady Przykład 1. Słownik /* Slownik polsko-angielski. Program czyta wyraz polski i podaje jego tlumaczenie na jezyk angielski */ // dyrektywy preprocesora #include <iostream> // we-wy podstawowe #include <fstream> // działanie na plikach #include <cstring> // działanie na napisach w stylu języka C struct WYRAZ char polski[20]; char angielski[20]; ; const int MAXW = 1000; // maksymalna liczba wyrazów w słowniku const int MAXDL = 20 const int MAXZN = 80 // maksymalna długość wyrazu // maksymalna liczba znaków w wierszu // prototypy funkcji WYRAZ *CzytajSlownik(char nazwapliku[]); bool znalezionowyraz(wyraz slownik[], char polski[], char angielski[]); /*************************************************************************/ /***** Główna funkcja programu *************/ /*************************************************************************/ int main() WYRAZ *slownik; char polski[maxdl], angielski[maxdl]; // Wczytaj słownik z pliku slownik = CzytajSlownik("slownik.DAT"); if (!slownik) cout << "*** Nie mozna wczytac slownika ***\n"; return 1; // Tłumacz bool koniec = false; while (!koniec) cout << "Wpisz wyraz lub 'k' aby zakonczyc ==> "; cin >> polski; cin.ignore(80, '\n'); //ignoruje 80 znaków lub resztę wiersz if (strcasecmp(polski, "k") == 0) koniec = true; else if (znalezionowyraz(slownik, polski, angielski)) cout << angielski << "\n\n"; else cout << polski << " -- nie ma w slowniku.\n\n"; delete [] slownik; 22

/*************************************************************************/ /***** Funkcje *************/ /*************************************************************************/ WYRAZ *CzytajSlownik(char nazwapliku[]) // Funkcja czyta plik słownika. // Zwraca true, jeśli operacja czytania się powiodła, // w przeciwnym wypadku zwraca false. WYRAZ *slownik; // otwórz plik słownika ifstream we(nazwapliku); if (!we) slownik = new WYRAZ[MAXW]; if (!slownik) // wczytaj i wyświetl wiersz nagłówkowy char wiersz[maxzn]; we.getline(wiersz, MAXZN); cout << wiersz << endl; // wczytaj wyrazy i ich tłumaczenia do tablicy słownika int i = 0; while (i < MAXW && we >> slownik[i].polski >> slownik[i].angielski) we.ignore(maxzn, '\n'); i++; // wyświetl liczbę wczytanych wyrazów *slownik[i].polski = '\0'; cout << " (" << i << " wyrazow )\n\n"; return slownik; bool znalezionowyraz(wyraz *slownik, char polski[], char angielski[]) // Wyszukuje słowo w słowniku // slownik - tablica slownika // polski - wyraz tłumaczony // angielski - wyraz przetłumaczony // Zwraca true, jeśli wyraz został znaleziony, false w przeciwnym wypadku. bool znaleziony = false; for (int i = 0;!znaleziony && *slownik[i].polski!= '\0'; i++) if (strcasecmp(slownik[i].polski, polski) == 0) strcpy(angielski, slownik[i].angielski); znaleziony = true; return znaleziony; 23

Przykład 2: rozdawanie kart (Deitel&Deitel, Arkana C++ Programowanie, RM, 1998, str. 786) #include <iostream> #include <iomanip> #include <cstdlib> // dla srand(), rand() #include <ctime> // dla time() struct Karta char *figura; char *kolor; ; const int ILEKART = 52; void weztalie( Karta *, char *[], char *[] ); void tasuj( Karta * ); void rozdaj( Karta * ); int main() Karta talia[ ILEKART ]; char *figura[] = "as", "dwojka", "trojka", "czworka", "piatka", "szostka", "siodemka", "osemka", "dziewiatka", "dziesiatka", "walet", "krolowa", "krol" ; char *kolor[] = "kier", "karo", "pik", "trefl" ; srand( time( 0 ) ); weztalie( talia, figura, kolor ); tasuj( talia ); rozdaj( talia ); void weztalie( Karta *wtalia, char *wfigura[], char *wkolor[] ) // przypisz karty talii for ( int i = 0; i < ILEKART; i++ ) wtalia[ i ].figura = wfigura[ i % (ILEKART/4)]; wtalia[ i ].kolor = wkolor[ i / (ILEKART/4)]; void tasuj( Karta *wtalia ) for ( int i = 0; i < ILEKART; i++ ) int j = rand() % ILEKART; Karta temp = wtalia[ i ]; wtalia[ i ] = wtalia[ j ]; wtalia[ j ] = temp; void rozdaj( Karta *wtalia ) for (int i=0; i<4; i++) cout << setiosflags(ios::right) << setw(10) << "gracz "<< i << " " << ( ( i + 1 ) % 4? ' ' : '\n' ); for ( int i = 0; i < ILEKART; i++ ) cout << setiosflags(ios::right) << setw(10) << wtalia[ i ].figura << " " << setiosflags(ios::right) << setw(5) << wtalia[ i ].kolor << ( ( i + 1 ) % 4? ' ' : '\n' ); 24