Programowanie w języku Delphi » Artykuły

Łańcuchy

  • 2013-01-13 14:35
  • 8 komentarzy
  • 13751 odsłon
  • Oceń ten tekst jako pierwszy
Ten artykuł wymaga dopracowania!

Jeżeli możesz popraw ten artykuł według zaleceń, które możesz znaleźć na stronie Artykuły do poprawy. Po dopracowaniu tego tekstu można usunąć ten komunikat.


W tym artykule omówię funkcję operujące na tekście. Będą one umożliwiały takie działania jak dodawanie w wybrane pozycje jakichś znaków, odnajdywanie określonych wyrazów, zmianę tekstu na liczby (lub odwrotnie) i inne.

Spis treści

     1 Przydatne Metody
          1.1 Copy
               1.1.1 LeftStr, RightStr
          1.2 Length
          1.3 Pos
          1.4 PosEx
               1.4.1 Lewostronne PosEx
          1.5 Delete
          1.6 Insert
          1.7 Format
          1.8 ExtractStrings
          1.9 Inne
               1.9.1 Concat
               1.9.2 StringOfChar
               1.9.3 Trim
               1.9.4 UpperCase, LowerCase
               1.9.5 ReverseString
          1.10 Ćwiczenie
     2 Konwersje łańcuchów
          2.1 Zmiana z/na liczby
          2.2 Kody znaków ASCII
          2.3 Wartości Logiczne
          2.4 Data
               2.4.1 StrToDate
               2.4.2 StrToTime
     3 Zakończenie



Przydatne Metody


Copy


Copy służy ono do kopiowania części znaków ze zmiennej.

var
  S, Finall : String;
begin
  S := 'Adres strony: http://4programmers.net';
 
  Finall := Copy(S, 1, 6) + 'e-mail: [email protected]';
 
  ShowMessage(Finall);
end;

Mamy sobie dwa stringi. Teraz do drugiego kopiujemy kawalek tekstu z pierwszego. W poleceniu Copy pierwszym parametrem musi być źródło wykonywanej operacji, drugi parametr to miejsce od którego będzie się zaczynała operacja kopiowania, a ostatni to ilość znaków do skopiowania.

LeftStr, RightStr


Te funkcje zwracają kawałek tekstu po odpowiednio lewej i prawej stronie. Drugim parametrem jest ilość znaków które zostaną skopiowane. Funkcje różnią się tylko tym, że w LeftStr kopiowanie zaczyna się od lewej strony, a w RightStr od prawej.

var
  S : String;
begin
  S := ' Jeden Dwa Trzy Cztery Pięć';
  ShowMessage(LeftStr(S, 6));    //Zobaczymy "Jeden"
  ShowMessage(RightStr(S, 11));  //Zobaczymy "Cztery Pięć"
end;


Jeżeli łańcuch zawiera wielobajtowy tekst, funkcja może zwrócić inną ilość znaków niż podano w parametrze. Funkcjami pochodnymi są LeftBStr oraz RightBStr, które traktują wszystkie znaki jako jednobajtowe.

Length


Dość często używana funkcja, to Length. Zwraca długość tekstu.
var
  S : String;
begin
  S := 'Delphi';
  Length(S);  //Zwróci 6
end;


Length służy też do zwracania ilości elementów w tablicy.

Pos


Kolejne bardzo przydatne polecenie. Otóż umożliwia ono przeszukiwanie określonego ciągu znaków w jakiejś zmiennej tekstowej. Przykładowo chciałbyś w jakiejś zmiennej odnaleźć spacje.
var
  S : String;
begin
  S := 'Tekst zawierający spacje';
  if Pos(' ', S) > 0 then  //Jeżeli ciągu znaków nie ma w zmiennej, Pos zwróci 0
    ShowMessage('W zmiennej znajdują się spacje.');
end;

W poleceniu Pos pierwszym parametrem jest szukany znak, a drugim źródło poszukiwań, czyli jak w naszym przykładzie zmienna S. W powyższym przykładze program poszukuje spacji w zmiennej. Jeżeli pozycja spacji będzie większa niż 0 (tzn. jeżeli tak jest) to wyświetla komunikat.
Polecenie Pos używa się często w połączeniu z innymi - pokażemy to dalej...


PosEx


Następne polecnie jest bardzo podobne do zwykłego Pos'a z tym, że posiada pewien dodatkowy parametr - miejsce od którego chcemy zacząć szukać.
var
  S: String;
begin
 s:= 'aaaa * bbbb * cccc';
 PosEx('*', s, 7);
end;
Otrzymamy wynik: 13, kiedy Pos() zwróciło by nam 6.

Pos(S, S2);
//jest równoważne
PosEx(S, S2, 1);


Lewostronne PosEx


Wiem z doświadczenia że czasami przy zabawie z łańcuchami przydałoby się znaleźć jakiś tekst który znajduje się nie za tekstem odniesienia (tak jak w przypadku funkcji Pos i PosEx) a przed nim. Takiej funkcji nie ma w potężnych modułach VCL, więc napisałem ją sobie sam.

function PosExBack(aSubStr, aStr: String; aOffset: Integer): Integer;
var Res: Integer;
begin
  Result := 0;
  Res := 0;
  aStr := Copy(aStr, 1, aOffset);
  repeat
    Res := PosEx(aSubStr, aStr, Res + 1);
    if Res > 0 then Result := Res;
  until Res = 0;
end;


Działa tak samo jak zwykłe PosEx, tylko że nie szuka tekstu za aOffset tylko przed nim.

Delete


Kolejne polecenie służy do usuwania określonego ciągu znaków ze zmiennej. Pierwszym jego parametrem jest zmienna, której dotyczyć będzie operacja, drugim od jakiego miejsca w zmiennej będzie dotyczyć usuwania, a ostatni parametr to ilość znaków do usunięcia:

var
  S : String;
begin
  S := 'Adres strony: http://4programmers.net';
  Delete(S, 1, 21);
 
  ShowMessage(s);
end;

Powyższa procedura wyświetli jedynie napis 4programmers.net (bez http://). Gdy już wiesz o co chodzi w poleceniu Delete oraz Pos można napisać procedurę usuwającą wszystkie spacje w zmiennej:

var
  S : String;
begin
  S := 'Tekst zawierający spacje';
  while Pos(' ', S) > 0 do
    Delete(S, Pos(' ', S), 1);
 
  ShowMessage(S);
end;

Powyższe komendy usuną wszystkie spacje w zmiennej i ponownie wyświetlą stringa. Zastosowanie tutaj pętli spowoduje, że na pewno wszelkie spacje zostaną usunięte.


Insert


Parametry w tej procedurze są trochę pomieszane dlatego, że jej pierwszym parametrem jest ciąg znaków, który ma być wstawiony, drugim jest źródło operacji, czyli zmienna, której dotyczyć będzie operacja, a ostatni - trzeci parametr to znak od którego zaczynać się będzie operacja dodawania ciągu:

var
  S : String;
begin
  S := 'Adres strony: 4programmers.net';
  Insert('http://', S, 15);
 
  ShowMessage(s);
end;



Format


Ta funkcja jest bardzo przydatna, gdyż umożliwia formatowanie tekstu.

var
  ImieKolegi, MojeImie: String;
  Wiek: Integer;
begin
  ImieKolegi := 'Bożydar';
  MojeImie := 'Gerwazy';
  Wiek := 17;
  ShowMessage(Format('Witaj %s, nazywam się %s i mam %d lat', [ImieKolegi, MojeImie, Wiek]));
end;

Zobaczymy oczywiście Witaj Bożydar, nazywam się Gerwazy i mam 17 lat. Może wygląda to skomplikowanie, ale w rzeczywistości to bardzo proste. Pierwszy parametr to tekst, która ma w sobie dziwne człony (mówię o %s i %d). W te miejsca zostaną po kolei ? wepchnięte ? łańcuchy z drugiego parametru. A co znaczą litery s i d? Te symbole odpowiadają odpowiednio stringowi i liczbie rzeczywistej. Więcej informacji w artykule Format.


ExtractStrings


Funkcja rozdziela łańcuch (trzeci parametr) według separatora/ów (pierwszy parametr), pomija tzn. "white spaces" (drugi parametr), i zapisuje do jakiejś zmiennej (czwarty parametr) np do TStrings, TStringList etc.

var
  TS: TStringList;
begin
  ExtractStrings([' '], [], 'jacek wacek placek lol', Memo1.Lines);
end;

Taki kod zapisze do Memo tekst:
jacek
 wacek
 placek
 lol

gdyby w drugim parametrze zamiast [] podać [#32] (kod spacji, czyli po prostu [' ']) tekst wyglądały tak:
jacek
wacek
placek
lol


Inne


Istnieją jeszcze mniej przydatne polecenia, rzadziej używane. Wymienię je poniżej.

Concat


Na początek polecenie Concat umożliwiające połączenie określonych wyrazów (bloków). Polecenie to jest bardzo rzadko stosowane bo zastępuje ono częściej używany operator + służący właśnie do łączenia znaków. Oto przykład:

var
  S: String;
begin
  S := '15 czerwca';
  ShowMessage(Concat('Dzisiaj jest ', S, ' roku 2012'));
end;

Nic nadzwyczajnego. Jedyne chyba jej zastosowanie to efekt estetyczny kodu, jeżeli łączymy więcej stringów.


StringOfChar


Nie robi ono nic specjalnego. Wpisz do programu taki kod:
ShowMessage(StringOfChar('A', 5));
a zobaczysz AAAAA.

Bardzo podobną funkcją jest DupeString, z tym że umożliwia ona kopiowanie całych łańcuchów (nie tylko pojedynczych znaków).


Trim


Funkcja nie robi nic oprócz obcinania spacji na początku i na końcu stringa:

var
  S : String;
begin
  S := ' Adam Boduch ';
  ShowMessage(Trim(S)); //Zobaczymy samo "Adam Boduch"
end;



UpperCase, LowerCase


Te funkcje zmieniają litery w łańcuchu na duże (UpperCase) oraz na małe (LowerCase) litery.

ShowMessage(UpperCase('http://4programmers.net'));  //Zobaczymy "HTTP://4PROGRAMMERS.NET"
ShowMessage(LowerCase([email protected]'));        //Zobaczymy "[email protected]"


ReverseString


Jak można wywnioskować po nazwie, funkcja zwraca odwrócony ciąg znaków. Po wywołaniu tego:
var
  S : String;
begin
  S := ' Odwróć Mnie';
  ShowMessage(ReverseString(S));
end;
Zobaczymy okienko z napisem "einM ćórwdO".


Ćwiczenie


Myślę, że dobrym podsumowaniem tego co dotychczas zrobiliśmy będzie program, który 'wygrzebie' ze stringa adres e-mail:

var 
  AtPos, LeftSidePos, MailLength, I: Integer;
  sText: String;
begin
  AtPos := Pos('@', sText);
  if AtPos > 1 then
    begin
      LeftSidePos := AtPos;
      while (sText[LeftSidePos - 1] in ['a'..'z', 'A'..'Z', '0'..'9', '-', '_', '.']) do
        Dec(LeftSidePos);
 
      MailLength := 0;
      while (sText[AtPos + MailLength + 1] in ['a'..'z', 'A'..'Z',  '.']) do
        Inc(MailLength);
 
      ShowMessage(Copy(sText, LeftSidePos, AtPos - LeftSidePos + MailLength + 1));
    end
      else
    ShowMessage('Podany tekst nie ma w sobie maila');
end;


Budowa nie jest zbyt skomplikowana. Na początek program w zmiennej odnajduje znak małpy, a później 'cofa się' w zmiennej aż napotka znak który nie może znajdować się w adresie. Następnie idzie od małpy do znaku który nie może znajdować się w domenie.

Konwersje łańcuchów



Zmiana z/na liczby


Do zmiany łańcucha na liczby i odwrotnie służą funkcje StrToInt oraz IntToStr.
var
  S: String;
  I: Integer;
begin
  S := '25';
  I := StrToInt(S);  //Teraz w I jest liczba 25
  ShowMessage(IntToStr(I));
end;

A co w przypadku jeżeli w S nie będzie liczby, albo będą oprócz niej inne znaki, np siema? W takim wypadku kompilator wyrzuci nam błąd 'siema' is not vaild Integer value. Możemy się przed tym zabezpieczyć dzięki takim funkcjom jak TryStrToInt lub StrToIntDef.

Istnieją odpowiedniki tych funkcji dla liczb zmiennoprzecinkowych, a są to StrToFloat oraz FloatToStr:
var
  S: String;
  D: Double;
begin
  S := '25.5';
  D := StrToFloat(S);  //Teraz w D jest liczba 25.5
  ShowMessage(FloatToStr(D));
end;

Dla tych funkcji również są ? bezpieczne ? odpowiedniki: TryStrToFloat i StrToFloatDef.

Kody znaków ASCII


Do zabawy takimi kodami służą funkcje Ord oraz Chr. Zamieniają one kolejno, znak na jego kod oraz odwrotnie.

var
  C: Char;
  I: Integer;
begin
  C := 'A';
  ShowMessage(IntToStr(Ord(C))); //Zobaczymy "65" > kod znaku "A";
 
   {Najpierw zamienimy to co jest w C ("A") na kod, następnie powiększymy o dwa, i znów zapiszemy}
  C := Chr( Ord(C)  +  2);  //Zwróci "C"
end;



Wartości Logiczne


Nie dla każdego te słowa są zrozumiałe. Wartości Logiczne to True/False (Prawda/Fałsz). Przykład takiej konwersji:

begin
  BoolToStr(True); //Zwraca "0"
  BoolToStr(False); //Zwraca "-1"
end;

Ta funkcja posiada jeszcze jeden parametr, jeżeli przyjmie on wartość True wtedy zwróci nam angielskie słowa odpowiadające wartościom.

begin
  BoolToStr(True,  True); //Zwraca napis "True"
  BoolToStr(False, True); //Zwraca napis "False"
end;



Data


Metod do wyświetlania, przechowywania, edytowania etc. dat jest tyle (7 stron, heh), że nie będę ich tu przepisywał i wymienię tylko kilka. Wszystkie są dostępne w module DateUtils.
 

StrToDate


Te funkcja służy do konwertowania właśnie daty na tekst.

Funkcja Date zwraca aktualną datę.
begin
  ShowMessage(DateToStr(Data));
end;

Odpowiednikiem zmieniający tekst na datę jest StrToDate.

StrToTime


Z kolei ta, jak się nie trudno domyśleć konwertuje czas na tekst.

Funkcja Time zwraca aktualny czas.
begin
  ShowMessage(TimeToStr(Time));
end;

Odpowiednikiem jest StrToTime.

Zakończenie


Warto zapoznać się zarówno z modułem StrUtils jak i z pozostałymi. Nie po to żeby wykuć wszystko co tam jest, tylko żeby popatrzeć na to co w nich siedzi, i poczytać dwa słowa. Często się zdarza że piszemy jakieś funkcje nie mając pojęcia, że programiści Delphi już o tym pomyśleli i odwalili robotę za nas. Więc warto trochę poczytać i douczyć się tylko po to żeby sobie oszczędzić czasu.

8 komentarzy

tweety 2012-11-07 22:50

bezsensowne jest korzystanie z concat jak by nie można było po prostu napisać s+s

Patryk27 2012-06-15 17:52

Cd.konstrukcji for..in to w przypadku string'a na 10000000 iteracji jest ona szybsza o około 600 milisekund. A w przypadku stringów dosyć długich (tzn.większych od 255 znaków), są to nawet sekundy szybciej. (FPC 2.6.0).

Patryk27 2012-06-15 17:46

@Tezcatlipoca: we FPC 2.6.0 robi (nie wiem, czy Delphi takie coś obsługuje)... I to znaczną (tzn.jeżeli wywołujemy tego for'a miliony razy na sekundę; inaczej jest to po prostu bardziej czytelniejsza konstrukcja). A nawet, jeżeli w przypadku string'ów nie ma dużej różnicy, to przy tablicach dynamicznych konstrukcja for..in jest o wiele wydajniejsza.

msm 2012-06-15 16:49

@Patryk27 - (z ciekawości świata i innych języków:) to naprawdę robi różnicę? Bo na logikę te dwa kody powinny być równoważne.

Patryk27 2012-06-15 16:18

Tak na marginesie, jeżeli zależy nam na szybkości, zamiast używać For I := 1 To Length(string) Lepiej jest użyć For Char in String :)

iYYa24 2005-07-29 20:54

Spoko arcik, tylko ostatni przyklad niejest zaabardzo dopracowany, alemtakto spoko

Waldi 2004-02-29 17:17

Poważny błąd w ostatnim pzykładzie ponieważ kopiuje kawałek adresu od początku do małpy a po małpie już nie potrzeba by jeszcze 1 pętelkę która by szła po małpie aż napotka spacje