11. OPERACJE NA ŁAŃCUCHACH ZNAKOWYCH
11.1. Opis typu i deklarowanie zmiennych łańcuchowych
Typ łańcuchowy o nazwie string opisuje strukturę danych, złożoną z 255 elementów typu Char. Jest ona przeznaczona do pamiętania łańcuchów znakowych, czyli fragmentów tekstu. W jednej zmiennej typu string można przechować najwyżej 255 znaków. Nie wszystkie stojące do dyspozycji miejsca muszą być wykorzystane. Odróżniamy długość deklarowaną zmiennej łańcuchowej od jej długości rzeczywistej.
Format zmiennej łańcuchowej w pamięci ilustruje rysunek:
L |
|
|
|
|
|
|
|
|
|
|
|
L[0] |
L[1] |
L[2] |
L[3] |
L[4][ |
L[5] |
L[6] |
L[7] |
L[8] |
L[9] |
... |
L[255] |
#7 |
'P' |
'r' |
'o' |
'g' |
'r' |
'a' |
'm' |
|
|
... |
|
Szczególną rolę odgrywa zerowa pozycja zmiennej łańcuchowej. Przechowuje ona zawsze znak o numerze równym aktualnej długości łańcucha. W podanym przykładzie będzie tam się znajdować znak #7.
Deklaracja zmiennej łańcuchowej:
var Napis: string;
Nadanie zmiennej wartości początkowej:
const L:string = 'Program';
11.2. Własne typy łańcuchowe
Można zdefiniować własny typ łańcuchowy o odpowiednio mniejszej długości deklarowanej. Na przykład:
type S_80 = string[80];
Zmienna typu S_80 zajmuje 81 bajtów pamięci (bajt zerowy + 80 bajtów deklarowanych).
11.3. Stałe łańcuchowe
Jawne stałe łańcuchowe umieszcza się pomiędzy dwoma apostrofami:
'Turbo Pascal'
'Adam Mickiewicz'
'127'
'B:\JEZYKI\TP\TURBO.EXE'
''
Dwa kolejne apostrofy reprezentują łańcuch pusty. Jego aktualna długość wynosi 0. W bajcie zerowym takiego łańcucha znajduje się znak #0.
Definiowane stałe łańcuchowe są przydatne, gdy w programie często występuje taki sam łańcuch o znacznej długości:
const C1='Nacisnij klawisz <Enter>';
C2='Wprowadz liczbe: ';
W dowolnym miejscu bloku, w którym definicja obowiązuje, nazwa C1 będzie rozumiana jako wartość łańcuchowa 'Nacisnij klawisz <Enter>', a nazwa C2 - jako wartość łańcuchowa 'Wprowadz liczbe: `.
11.4. Wykorzystanie instrukcji przypisania
Łańcuchy krótsze od deklarowanej długości zmiennej, lub tej samej jak ona długości, są kopiowane bez zmian. Natomiast, gdy chcemy przypisać łańcuch dłuższy, niż na to pozwala deklarowana długość zmiennej, to końcowe znaki łańcucha zostają po prostu obcięte:
type S20=string[20]'
S12=string[12];
var Nazwisko: S20;
Miasto:S12;
begin
Nazwisko:='Kowalski'; {Nazwisko='Kowalski'}
Miasto:='Konstantynopol';{Miasto='Konstantynop'}
end.
Do łańcucha wolno przypisać znak. Na przykład, jeżeli:
var Z:Char; L:string;
to prawidłowe jest przypisanie:
L:=Z;
W wyniku tej operacji L będzie łańcuchem o aktualnej długości równej 1. Natomiast przypisanie odwrotne jest niemożliwe, bo zmienna typu Char ma do dyspozycji jeden bajt pamięci, podczas gdy zmienna typu string wymaga 256 bajtów pamięci!
11.5. Odwoływanie się do elementów łańcucha
Do elementów (poszczególnych znaków) łańcucha można odwoływać się tak samo, jak do elementów jednowymiarowej tablicy. Takie odwołania mogą być przeznaczone zarówno do zapisu, jak odczytu znaków..
Chcąc odczytać K-ty znak łańcucha Lancuch i zapamiętać ten znak w zmiennej znakowej C, napiszemy::
C:=Lancuch[K];
Przykład: Procedura wyprowadzająca łańcuch W drukiem rozstrzelonym:
procedure Pisz_szeroko(W:string); begin for K:=1 to Length(W) do begin Write W[K]; Write(#32); end; end; |
11.6. Operacja sklejania łańcuchów
Chcąc skleić dwa lub więcej łańcuchów, czyli połączyć je w jeden wspólny łańcuch, stosujemy operator sklejania (konkatenacji), który wygląda tak samo, jak operator dodawania. Otrzymane w ten sposób wyrażenie łańcuchowe można przypisać do zmiennej łańcuchowej. Oczywiście wynik takiej operacji zależy od porządku składników. Argumentami operacji sklejania mogą być również znaki, np.:
const C1='Jan'; C2='Kowalski';
var S1,S2: string;
begin
S1:=C1+#32+C2; {S1='Jan Kowalski'}
S2:=C2+#32+C1; {S2='Kowalski Jan'}
end;
11.7. Porównywanie łańcuchów
Porównywanie łańcuchów odbywa się za pomocą operatorów relacyjnych, działających na łańcuchach:
=,<>,<,>,<=,>=
Łańcuch mniejszy, to ten, który na wcześniejszej pozycji ma znak o mniejszym numerze porządkowym. Wynika stąd na przykład, że łańcuch pisany wielkimi literami jest mniejszy od tego samego łańcucha pisanego małymi literami.
Łańcuchy są równe, gdy mają tę sama liczbę znaków i jednocześnie znaki na każdej ich pozycji są jednakowe.
W przykładzie 11.5 pokazano wykorzystanie operatora większości w procedurze, która sortuje zawartość tablicy łańcuchów w porządku alfabetycznym. W programie pokazano także sposób zainicjowania tablicy łańcuchów przy jej deklarowaniu.
program Ex11_5; uses Crt;
type Tab=array [1..6] of string;
const T:Tab=('Tomasz','Jacek','Adam','Robert','Ewa','Beata');
procedure PokTab(var T:Tab); var I:Integer; begin for I:=Low(T) to High(T) do Writeln(T[I]); Writeln; end;
procedure Sortuj(var T:Tab); var K,J:Integer; Kopia:string; begin for K:=Low(T) to High(T)-1 do for J:=K+1 to High(T) do if T[K]>T[J] then begin Kopia:=T[K]; T[K]:=T[J]; T[J]:=Kopia; end; end;
begin Clrscr; Poktab(T); Sortuj(T); Poktab(T); Readln; end. |
11.8. Funkcje i procedury standardowe operujące na łańcuchach
Poniżej pokazano nagłówki definicji i krótki opis działania standardowych funkcji i procedur Turbo Pascala, przeznaczonych do operacji na łańcuchach.
function Length(S:string):Integer;
Zwraca aktualną długość łańcucha S.
function Concat(S1,S2, . . . Sn):string;
Zwraca łańcuch będący sklejeniem kolejnych argumentów S1, S2, … Sn. Zamiast tej funkcji można zastosować operatory sklejania `+'.
function
Copy(S:string;Poz:Integer;D:Integer):string;
Łańcuch wynikowy jest wycinkiem łańcucha S zaczynającym się od pozycji Poz, mającym długość określoną przez argument D.
function Pos(S1,S2:string):Byte;
Bada, czy w łańcuchu S2 znajduje się podłańcuch S1. Jeżeli nie ma takiego podłańcucha, zwraca wartość 0; w przeciwnym przypadku zwraca numer pozycji S2, od której rozpoczyna się pierwsze wystąpienie podłańcucha S1.
procedure
Delete(var S:string; Poz:Integer; D:Integer);
Wycina podłańcuch z łańcucha S. Wycięty podłańcuch zaczyna się od pozycji Poz i ma D znaków. Jeżeli Poz jest większa od aktualnej długości S, to postać łańcucha S nie ulega zmianie.
procedure
Insert(S1:string; var S2:string; N:Integer);
Wstawia do łańcucha S2 podłańcuch S1, począwszy od pozycji następnej za znakiem N-tym łańcucha S2. Jeżeli N jest większe od aktualnej długości S2, to podłańcuch S1 zostaje doklejony na końcu S2.
procedure Str(X; var S:string);
Przekształca daną X dowolnego typu liczbowego na łańcuch znaków S, reprezentujący odpowiedni zapis dziesiętny tej danej. Pisząc argument X, można stosować parametry określające liczbę pozycji zapisu i liczbę miejsc po kropce dziesiętnej, podobnie jak w instrukcjach Write, Writeln.
procedure Val(S:string; var X; var Kod:Integer);
Przekształca łańcuch znakowy S, stanowiący poprawny zapis dowolnej liczby, na liczbę X, będącą zmienną odpowiedniego typu liczbowego. Jeżeli Łańcuch S był poprawnym zapisem liczby, to argument wyjściowy Kod przyjmuje wartość 0; w przeciwnym przypadku Kod jest numerem pozycji łańcucha S, na której wykryto pierwszy błąd zapisu.
11.9. Przykłady wykorzystania standardowych funkcji i procedur łańcuchowych
Przykład 11.6. Zastosowanie procedury Val do programowej kontroli formatu liczby
procedure CzytKontrol(var X:Real); var S:string[80]; Kod:Integer; begin repeat Write('Podaj liczbe: '); Readln(S); Val(S,X,Kod); if Kod<>0 then Writeln('Blad na pozycji ',Kod); until Kod=0; end;
|
Przykład 11.7. Znajdowanie liczby wystąpień podłańcucha w łańcuchu
program Ex11_7; uses Crt; type S_80 = string[80]; const L:S_80 = 'Adam Kowalski i Janina Kowal-Kowalska'; var P:S_80; W:Byte;
function IlePodlanc(P,L:string):Byte; {Zwraca liczbę wystąpień podłańcucha P w łańcuchu L.} var Licznik:Byte; Pozycja,D:Integer; begin D:=Length(P); {Długość podłańcucha} Licznik:=0; while Pos(P,L)<>0 do begin Pozycja:=Pos(P,L); {Pozycja 1. wystąpienia P} Licznik:=Licznik+1; {Licznik wystąpień P w L} Delete(L,Pozycja,D); {Usuwanie kolejnego P} end; IlePodlanc:=Licznik; end;
begin Clrscr; Writeln(L); Write('Wpisz szukany podlancuch: '); Readln(P); W:=IlePodlanc(P,L); Write('Liczba wystapien: ', W); Readln; end. |
Przykładowa zmiana argumentu L podczas liczenia wystąpień podłańcucha `Kowal':
Adam Kowalski i Janina Kowal-Kowalska {postać początkowa}
Adam ski i Janina Kowal-Kowalska (po kroku nr 1}
Adam ski i Janina -Kowalska {po kroku nr 2}
Adam ski i Janina -ska {po kroku nr 3}
Przykład 11.8. Użycie funkcji Length do centrowania tekstu w elementach tablicy łańcuchów
program Ex11_8; uses Crt; type S_80 = string[80]; Tab = array [1..6] of S_80;
const T:Tab= ('Ordinal types are a subset of simple types.', 'All simple types other than real types', 'are ordinal types.', 'Except for integer-type values,', 'the first value of every ordinal type', 'has ordinality 0.');
procedure Poktab(var T:Tab); var K:Integer; begin for K:=Low(T) to High(T) do Writeln(T[K]); Writeln; end;
procedure Centruj(var T:Tab); var K:Integer; S:string; begin for K:=Low(T) to High(T) do begin S:=''; repeat S:=S+#32; until 2*Length(S)+Length(T[K])>=79; T[K]:=S+T[K]; end; end; begin Clrscr; Centruj(T); PokTab(T); Readln; end. |
W procedurze Centruj, łańcuch S, początkowo pusty, jest konstruowany przez kolejne doklejanie pojedynczych spacji w zagnieżdżonej pętli repeat-until. Dla każdego elementu T[K] doklejanie spacji kończy się, gdy suma podwójnej długości łańcucha S oraz długości łańcucha T[K] osiągnie wartość, odpowiadającą liczbie znaków, mieszczących się w jednym wierszu ekranu. Ma to miejsce po spełnieniu relacji:
2*Length(S)+Length(T[K]}>=79
W rezultacie, fragmenty tekstu, zawarte w kolejnych elementach tablicy, zostaną wypisane symetrycznie względem środka ekranu, jak pokazano poniżej.
Ordinal types are a subset of simple types.
All simple types other than real types
are ordinal types.
Except for integer-type values,
the first value of every ordinal type
has ordinality 0.
83