LEKCJA 3 - OPERACJE NA ŁAŃCUCHACH

Ostatnia lekcja była bardzo ważna. Poznałeś pojęcie zmiennej i podstawowe jej typy. Ta lekcja będzie pewnym uzupełnieniem, gdyż w poprzednich przykładach operowaliśmy tylko na liczbach. Konkretnie był to typ int (liczby całkowite) i float (liczby rzeczywiste). Zmienne tych typów mogą przyjmować wartości tylko z określonych przedziałów liczbowych. Dla prostych programów nie ma to większego znaczenia, ale dobrze jest wiedzieć, że te podstawowe zakresy są dosyć wąskie. Tak więc, zanim przejdziemy do operacji na łańcuchach znaków, krótkie podsumowanie typów liczbowych:

typ zmiennejzakres
int-32 768 do 32 767
long-2 147 483 648 do 2 147 483 647
float3,4* 10^(-38) do 3,4*10^(38)
double1,7* 10^(-308) do 1,7*10^(308)
long double3,4* 10^(-4932) do 3,4*10^(4932)

Zapis 10^ oznacza "10 do potęgi". Należy więc zauważyć, że przy niektórych typach mamy bardzo szerokie możliwości. Warto jednak wiedzieć, że w różnych implementacjach języka typy te mogą nieco się różnić oferowanym zakresem lub nazywać się nieco inaczej (choć ja się z tym jeszcze nie spotkałam).

Zajmiemy się teraz typem znakowym, który będzie istotą tej lekcji. Jak wygląda deklaracja zmiennych tego typu już wiemy: char zmienna;. Wzorzec konwersji przy wyświetleniu lub wczytywaniu zmiennej typu char to %s dla łańcucha dłuższego niż 1 znak i %c dla łańcucha jednoznakowego.

Pora na przykład:

#include <stdio.h> 
#include <conio.h>
#include <string.h>
main (void) 
{ 
char znak1,znak2,znak3; //deklaracja znaków 
char *slowo1, *slowo2; //deklaracja słów 
clrscr(); 
znak1='a'; 
znak2=102; 
slowo1="Anna";

printf("Podaj znak3: \n"); 
scanf("%c",&znak3); 

printf("Podaj slowo2: \n"); 
scanf("%s",&slowo2); 

printf("Zmienne zawierają:\n"); 
printf("znak1, znak2,znak3: %c %c %c\n",znak1,znak2,znak3); 
printf("słowo1, słowo2: %s %s\n",slowo1, slowo2); 

getch(); 
return 0 ; 
} 

A oto przykładowa zawartość ekranu po wykonaniu programu:

Podaj znak3
q
Podaj slowo2 
programowanie
Zmienne zawierają:
znak1, znak2, znak3: a f q 
slowo1, slowo2: Anna programowanie

W przykładzie widoczne są niewielkie różnice pomiędzy deklaracją i przypisywaniem wartości łańcuchom jedno- i wieloznakowym. Tak więc, przypisać wartość zmiennej jednoznakowej możemy na kilka sposobów:

Szerszego omówienia wymaga jedynie drugi sposób. Otóż każdy znak widoczny na klawiaturze posiada swój kod (tzw. kod ASCII). Zamiast podawać określony znak w apostrofach można zatem podać tylko jego kod. Wydaje się to utrudnieniem, ale istnieją znaki, których nie ma na klawiaturze lub trudno je określić jednym znakiem (np. enter, escape, klawisze funkcyjne, kursory itp). Wtedy bez znajomości ich kodu nie da się ich opisać w programie. Użyty w przykładzie kod 102 dotyczy literki "f". Dokladny opis kodów znajdziesz tutaj.

Przypisanie wartości do zmiennej dla łańcucha dłuższego niż jeden znak odbywa się podobnie jak w pierwszym przypadku, z tą jednak różnicą, że zamiast apostrofów (') należy używać cydzysłowia ("). Prawda, że proste?


Podsumowując:

  1. Zmienne liczbowe mogą zawierać się w pewnych zakresach, których nie można przekraczać.
  2. Deklaracja zmiennej znakowej wygląda tak: char znak; a zmiennej łańcuchowej tak: char *slowo;
  3. Wartość zmiennej znakowej można przypisać w programie poprzez umieszczenie znaku w apostrofach lub przez napisanie jego kodu.
  4. Wartość zmiennej łańcuchowej można przypisać w programie poprzez umieszczenie wyrazu (lub kilku wyrazów) w cudzysłowie.
  5. Zmienne znakowe i łańcuchowe można wczytywać z klawiatury używając funkcji scanf i odpowiednich wzorców konwersji (%s dla ciągu znaków i %c dla pojedynczego znaku).
  6. Każdy znak posiada swój kod ASCII.
  7. Kod ASCII mają również znaki nie przedstawione na klawiaturze komputera. np. ß, ö, à.


Wracając jeszcze na chwilę do tematu kodów, napiszmy prosty program, który sprawdzi kod wciśniętego przez nas klawisza:

#include <stdio.h> 
#include <conio.h> 
#include <string.h> 
main (void) 
{ 
char znak;
int kod; 
clrscr();

printf("Wciśnij znak na klawiaturze: \n"); 
scanf("%c",&znak);
printf("Kod wciśniętego znaku to: %d \n",znak); 

printf("Podaj kod znaku: \n"); 
scanf("%d",&kod); 
printf("Znak o podanym kodzie to: %c \n",kod); 

getch(); 
return 0 ; 
} 

Przykładowa zawartość ekranu po wykonaniu programu:

Wciśnij znak na klawiaturze:
a
Kod wciśniętego znaku to: 97
Podaj kod znaku: 100
Znak o podanym kodzie to: d

Program ten jest bardzo dobrym przykładem na poznanie kodów używanych znaków i jednocześnie na użycie wzorców konwersji. Zauważmy, że wczytując znak funkcją scanf() używamy wzorca %c, ale żeby odczytać jej kod wystarczy w następnym komunikacie zmienić wartość wzorca na %d (pomimo, że zmienna znak jest typu char!).

Analogicznie sprawa się ma, jeżeli chcemy poznać znak odpowiadający danemu kodowi. Funkcją scanf() wczytujemy kod (używając wzorca %d), a w następnym komunikacie zmieniamy wzorzec na %c i w ten sposób uzyskujemy szukany znak.

Tak więc natura zmiennych znakowych jest jakby dwoista. Nie można powiedzieć tego o innych zmiennych - dla nich obowiązują tylko pojedyncze wzorce (łańcuch ma zawsze wzorzec %s, liczba całkowita ma zawsze wzorzec %d itd).

I jeszcze jedna mała uwaga: zauważmy, że liczby mogą być traktowane i jako liczby całkowite i jako łańcuchy znaków, ale NIE jednocześnie! Znaczy to, ze musimy z góry określić typ zmiennej, choć jej wartości będą na pozór identyczne. Na pozór, ponieważ a="123"; i a=123; to dwa różne przypisania. W pierwszym przypadku zmienna a jest to ciąg znaków, a w drugim jest to liczba.

To jeszcze nie wszystko co należy wiedzieć o łańcuchach.

Łańcuch nie musi być jednym słowem - może stanowić nawet całe zdanie, jednakże długość tego łańcucha nie może przekroczyć pewnej (dosyć sporej) ilości znaków. Pozostałe znaki zostaną zignorowane. A jak odczytać długość podanego łańcucha? Służy do tego funkcja strlen(). Przykład:

#include <stdio.h> 
#include <conio.h> 
#include <string.h> 
main (void) 
{ 
int dlugosc; 
char *lancuch; 
clrscr();

lancuch="Anna Miedzianowska"; 
dlugosc=strlen(lancuch); 
printf("Łańcuch '%s' ma: %d znaków \n",lancuch, dlugosc); 

printf("Pierwsza litera łańcucha to: %c \n",lancuch[0]); 
printf("Ostatnia litera łańcucha to: %c \n",lancuch[dlugosc-1]); 
printf("A ostatni znak łańcucha to: %c \n",lancuch[dlugosc]); 

getch(); 
return 0 ; 
} 

Przykładowa zawartość ekranu po wykonaniu programu:

Lancuch 'Anna Miedzianowska' ma: 18 znaków
Pierwsza litera lancucha to: A
Ostatnia litera lancucha to: a
A ostatni znak lancucha to:  
 

Używając funkcji strlen(), której argumentem był nasz łańcuch sprawdziliśmy jego długość. Wszystko okazało się zgodne z prawdą, łańcuch ten ma 18 znaków (licząc ze spacją, która też jest znakiem, mimo, że jej nie widać). Udało nam się także odczytać poszczególne litery łańcucha posługując się nawiasami kwadratowymi dopisanymi do nazwy zmiennej. Zapewne zdziwiło Cię to, że znak numer 18 łańcucha to nie jest 'a', lecz dziwny kwadrat, a nic takiego przecież nie wpisywaliśmy. Otóż w języku C w wielu przypadkach liczenie zaczyna się od zera, a nie od jedynki. Dlatego właśnie na pierwszy znak łańcucha wskazuje odwołanie: lancuch[0], na drugi: lancuch[1] itd. Co kryje się więc za pojęciem lancuch[18]? Ostatni znak łańcucha jest to zawsze znak \0. Wskazuje on na koniec łańcucha. Nie musisz się tym przejmować, ale zapamiętaj, że liczenie znaków zaczynamy zawsze od zera!

Potrafisz już odczytywać poszczególne znaki łańcucha. Posługując się operacją przypisania w prosty sposób można zmienić poszczególne znaki. Np. gdyby po przypisaniu tekstu "Anna Miedzianowska" do zmiennej lancuch napisać: lancuch[2]='i'; to nasza zmienna przechowywałaby już tekst "Ania Miedzianowska". W ten sposób można zmienić dowolną literę w łańcuchu.

Przy bardziej skomplikowanych zmiennych, zwłaszcza przy tworzeniu rekordów, ważne jest, aby nie szastać zbyt pamięcią przydzielaną dla zmiennych przy ich deklaracji. Nie znaczy to, że nie można deklarować zbyt dużo zmiennych. Chodzi raczej o to, by bliżej określić ich zawartość. Np. jeżeli do programu będziemy wczytywali imiona naszych znajomych, to z góry można przewidzieć, że zmienne te nie będą zawierały więcej niż 15 liter. Deklaracja ograniczonego łańcucha wygląda następująco: char lancuch[15];. Powrócimy do tego tematu w lekcjach dotyczących rekordów.

Jest wiele funkcji podobnych do strlen(), które potrafią działać na łańcuchach. Ciekawsze z nich to:
strcat() - łączy dwa łańcuchy,
strcmp() - porównuje dwa łańcuchy rozróżniając małe i duże litery,
strlwr() i strupr() - zamienia w danym łańcuchu duże litery na małe i odwrotnie,
strrev() - odwraca kolejność znaków w łańcuchu,
strset() - wypełnia łańcuch danym znakiem.

Użycie tych funkcji przedstawia następny przykład:

#include <stdio.h> 
#include <conio.h> 
#include <string.h> 
main (void) 
{ 
char *lancuch1, *lancuch2, *lancuch3; 
clrscr();
lancuch1="Anna";
lancuch2="Miedzianowska";

printf("Lancuch1 to: %s \n",lancuch1); 
printf("Lancuch2 to: %s \n",lancuch2); 

printf("\nZmieniamy duze litery na male: \n"); 
strlwr(lancuch1); 
printf("Lancuch1 wyglada teraz tak: %s \n",lancuch1); 

printf("\nZmieniamy male litery na duze: \n"); 
strupr(lancuch2); 
printf("Lancuch2 wyglada teraz tak: %s \n",lancuch2); 

printf("\nLaczymy dwa lancuchy: \n ); 
lancuch3=strcat(lancuch1,lancuch2); 
printf("Lancuch3 wyglada teraz tak: %s \n",lancuch3); 

printf("\nOdwracamy kolejnosc znakow w lancuchu: \n ); 
strrev(lancuch3); 
printf("Lancuch3 wyglada teraz tak: %s \n",lancuch3); 

printf("\nWypelniamy lancuch znakiem 'i':\n ); 
strset(lancuch3,i); 
printf("Lancuch3 wyglada teraz tak: %s \n",lancuch3); 

getch(); 
return 0 ; 
} 

A oto wygląd ekranu po wykonaniu programu:

Lancuch1 to: Anna
Lancuch2 to: 
Miedzianowska

Zmieniamy duze litery na male:
Lancuch1 wyglada teraz tak: anna

Zmieniamy male litery na duze:
Lancuch2 wyglada teraz tak: MIEDZIANOWSKA

Laczymy dwa lancuchy:
Lancuch3 wyglada teraz tak: annaMIEDZIANOWSKA

Odwracamy kolejnosc znakow w lancuchu
Lancuch3 wyglada teraz tak: AKSWONAIZDEIManna

Wypelniamy lancuch znakiem 'i':
Lancuch3 wyglada teraz tak: iiiiiiiiiiiiiiiii

Podsumowując:

  1. Łańcuch, który wygląda jak liczba nie jest nią! Istnieją aczkolwiek funkcje, które potrafią przekonwertować łańcuch liczbowy do postaci liczby.
  2. Mając dany łańcuch, możemy odczytać dowolny jego znak używając nawiasów kwadratowych. Pierwszy wpisany znak ma numer 0, a nie 1.
  3. Każdy ciąg kończy znak '\0'.
  4. Długość łańcucha można ograniczyć przy deklaracji, np.: char slowo[10];
  5. Łańcuchy można ze sobą porównywać, łączyć, odwracać w nich kolejność liter, zmieniać małe litery na duże i odwrotnie, a także przeszukiwać, kopiować na siebie itp. Nazwy funkcji, które to wykonują zawsze zaczynają się na 'str' (z angielskiego: string - łańcuch).


Autorem Kursu C++ jest Anna Miedzianowska (http://annamiedzianowska.republika.pl/).


Baner reklamowy: