W12 

Typy strukturalne cd

Typ String 
----------

Uwaga1:
  to nie jest standard Pascala! Tylko FreePascal, TP, BP, Delphi)

Uwaga2:
 Niektóre implementacje (np. FreePascal, Delphi) dopuszczają drugą implementację
 napisów, dozwalającą na napisy o długości do 2GB, tu skupiamy się na tradycyjnej
 implementacji napisów (o długości do 255 znaków).



Ten typ danych pozwala przechowywać napisy zmiennej długości.
Maksymalną dozwoloną długość napisu ustalamy podczas kompilacji
programu. Zmienne napisowe można deklarować na jeden z dwu poniższych 
sposobów:

  var
   s: String[ <rozmiar> ];

lub

  var
   s: String;

gdzie <rozmiar> jest liczbą z przedziału 1..255. Druga postać deklaracji jest 
równoważna podaniu rozmiaru równego 255. Rozmiar podawany w deklaracji jest 
największą dopuszczalną długością napisu, zatem deklaracja
    var s: String[10];
oznacza, że zmienna s może przechowywać napisy dowolnej długości od 0 do 10. 

Napis długości 0 to napis pusty. Aktualną długość napisu podaje funkcja Length,
jej argumentem jest napis. Można odwoływać się do poszczególnych znaków napisu, 
stosując taką samą składnię, jak w przypadku tablic. Poszczególne znaki 
napisu są indeksowane od 1. Oto przykład, fragment programu zamieniający 
wszystkie małe litery w napisie s na wielkie:

 var
   s: String;
   i: integer;
   {...}
 begin
   { ... }
   for i := 1 to Length(s) do
     s[i] := UpCase(s[i]);
   { ... }
 end;

Funkcja UpCase (tylko niektóre implementacje Pascala) dostaje jako argument znak i jako
wynik daje ten znak zamieniony na wielką literę (jeśli był literą, pozostałe znaki 
pozostawia bez zmian, przez literę rozumiemy tu literę alfabetu łacińskiego).

Zwróćmy uwagę w tym przykładzie na to, że poszczególne znaki napisu można
także zmieniać (a nie tylko odczytywać) i że są one typu char.

Na zmienne napisowe można przypisywać napisy:
  s1 := 'To jest napis';
  s2 := s1;
lub można ich wartości wczytywać z wejścia:
  readln(s1);
Kompilator zadba o to, by za długie napisy zostały obcięte do zadeklarowanej
maksymalnej długości zmiennej napisowej.

Uwaga na wczytywanie napisów operacją read(s)!

Oczywiście można także wypisywać zmienne napisowe:
   writeln(s);
Dla argumentów będących napisami zdefiniowano następujące operatory:
   = <> < <= > >=    (porównania leksykograficzne)
   +                 (łączenie napisów)

Na przykład:

   var
    s1, s2: String;
   begin
    s1 := 'ala ';
    s2 := 'ma kota';
    if s1 < s2
    then
      writeln('Jest mniejsze')
    Else
       writeln('Nie jest większe');
    writeln(s1 + s2);
   end.

Wypisze (kolejno):

Jest mniejsze
ala ma kota

Uwagi:
 1) Wynik operacji + jest obcinany do 255 znaków.
 2) W przypisaniach 
        <napis> := <wyrażenie napisowe>
    przypisywana wartość jest obcinana do maksymalnej długości napisu.
 3) Typ String może być wynikiem funkcji. Typ String[n] dopiero po nazwaniu.


W FreePascalu, TP/BP, Delphi zdefiniowano też wiele operacji działających 
na typie napisowym:

  function Copy(s: String; skąd: Integer; ile: Integer): String
Daje podnapis s zaczynający się na pozycji skąd, mający ile znaków. 
Jeśli skąd jest większe niż długość napisu s, to wynikiem jest pusty napis. 
Jeśli od miejsca skąd do końca napisu jest mniej niż ile znaków, to
kopiowane są wszystkie znaki do końca napisu.

  procedure Delete(var s: String; skąd: Integer; ile: Integer)
Wycina z s ile znaków zaczynając od pozycji skąd. Jeśli napis s jest 
za krótki, usuwa wszystkie znaki do końca napisu.

  procedure Insert(s1: String; var s: String; gdzie: Integer)
Wkleja do s napis s1 na pozycji gdzie.

  function Pos(co: String; s: String): Integer
Daje pozycję pierwszego wystąpienia napisu co w napisie s (lub 0). 
Rozróżnia małe i wielkie litery.

  procedure Val(s: String; var wart: Integer lub Real; var kod: Integer)
Stara się zamienić napis s na zapisaną nim liczbę. Jeśli się uda, przekazuje
tę liczbę jako wart, zaś kod ustawia na 0, wpp. kod ma wartość <> 0.

  procedure Str(wart [: Szerokość [: LiczbaMiejscPoPrzecinku]], var s: String)
Zamienia liczbę wart na jej postać tekstową i zapisuje ją w napisie s.


Reasumując: zmienne napisowe są bardzo wygodnym środkiem do operowania 
na napisach. Jedyną niedogodnością jest to, że trzeba z góry określić 
maksymalny rozmiar napisu, i że nie może on być większy od 255.

Implementacja zmiennych napisowych:

Typ napisowy String[n] jest realizowany jako tablica:
    array [0..n] of Char
Elementy tej tablicy od 1 do n służą do przechowywania 
znaków napisu, element o indeksie 0 służy do pamiętania długości napisu.
Ponieważ typ Char odpowiada wartościom od 0 do 255, maksymalna
długość napisów jest ograniczona do właśnie 255.


Przykłady programów wykorzystujących typ napisowy

Spróbujmy napisać własną funkcję zamieniającą liczby na napisy. 
Zrobimy to dla typu LongInt.

---> program wypliczb (zapis dziesiętny)
---> program wypliczb2 (zapis przy podstawie 2..10)