AnsiString
Adam Boduch
AnsiString - typ łańcuchowy języka Delphi.
Typ AnsiString
należy do tzw. długich łańcuchów, gdyż jego maksymalna długość to ~2^31 znaków, co przekłada się od 4 bajtów do 2 GB zajmowanej pamięci. Typ AnsiString obsługuje kodowanie 8bitowe (ANSI) oraz MBCS (Multibyte Character Set).
Przypisując wartość do zmiennej, tekst należy wziąć w apostrofy (') - przykład:
Zmienna := 'Wartość zmiennej';
W Delphi dla Win32, typ String wskazywał na typ AnsiString
; w Delphi dla .NET, typ String wskazuje na WideString
Na platformie Win32 przełącznik kompilatora {$H-}
powodował, iż typ String
był utożsamiany przez kompilator jako typ ShortString.
Typ AnsiString
jest tablicą znaków, tzn. do każdego elementu łańcucha (znaku) można odwoływać się podobnie jak do tablic:
program Foo;
{$APPTYPE CONSOLE}
uses
SysUtils;
const
Bar : AnsiString = 'Hello World';
var
i : Integer;
begin
for I := 1 to Length(Bar) do
Writeln(I, ': ', Bar[i]);
Readln;
end.
Na podstawie tego, co powiedzieliśmy, do dowolnego znaku można odwołać się poprzez Foo[n]
gdzie n
to numer znaku (1 oznacza pierwszy znak w łańcuchu).
const
Bar : AnsiString = 'Hello World';
var
S : AnsiChar;
begin
S := Bar[1];
end.
AnsiString to tablica (Char'ów) od 1 do n ([1..n]). N jest w tym przypadku liczbą znaków (długością), jaki i idneksem ostatniego elementu. Możemy się do niego w łatwy sposób odwołać za pomocą Length.
var
S: AnsiString = 'Ala ma kota';
begin
S[1] := 'U';
S[Length(S)] := 'y';
end.
Łańcuch AnsiString
jest zawsze zakończony znakiem #0 (niejawnie - nie jest wliczany do długości ciągu), dlatego można w łatwy sposób rzutować go na PChar. W przeciwieństwie jednak do tego drugiego, może zawierać znaki #0 (dla PChar
znak #0 oznacza koniec łańcucha).
W przeciwieństwie do ShortString, w AnsiString
nie ma możliwości manipulowania długością ciągu poprzez jego zerowy element. Po odwołanie się do niego wywołany zostanie błąd kompilatora [Error] Unit1.pas(32): Element 0 inaccessible - use 'Length' or 'SetLength'.
Pojedynczy znak AnsiString
wskazuje na typ AnsiChar. Mozna powiedzieć, że AnsiString
to zmodyfikowany PChar
. Alokacja pamięci jest jednak dokonywana niejawnie przez kompilator. Dodatkowo zawiera on informacje o długości, zajmowanej pamięci i tzw. licznik.
Struktura AnsiString
-12 bajtów (słowo) Wielkości przydzielonej pamięci | -8 bajtów (słowo) Licznik odwołań | -4 bajty (słowo) Długość | Łańcuch | Terminator (1 bajt)
Z licznikiem odwołań związana jest pewna osobliwość. Otóż gdy zadeklaruje się a := b
to obie wartości będą fizycznie wskazywały na ten sam napis (ich liczniki odwołań będą takie same). Gdy licznik odwołań spada do zera to pamięć jest zwalniana.
var
s1,s2: String; //Wszystkie liczniki to 0 (nie jest przydzielona pamięć)
begin
s1 := 'Napis'; //Liznik odwołań s1 to 1 (przydzielenie pamięci), s2 to 0 (nie jest przydzielona pamięć)
s2 := s1; //Licznik odwołań s1 i s2 to 2
s2 := s2 + ' i jeszcze troche dłuższy napis'; //Licznik odwołań s1 to 1, a s2 to 2
end. //Wszystkie liczniki to 0, zwalnianie pamięci
Z AnsString
związana jest skomplikowana gospodarka pamięci, która dzieje się bez wiedzy programisty. Powoduje to m.in., że lokalne zmienne typu AnsiString (w funkcjach czy procedurach) są inicjowane (otrzymują wartość '', czyli ?pusty ciąg znaków?) przy wejściu i zwalniane (o ile ich licznik odwołań nie zostanie zwiększony przez przypisanie do innej zmiennej globalnej) przy wyjściu z funkcji/procedury.
Z podobieństwa, AnsiString
i PChar
(terminator) wynika, że łańcuchy AnsiString
można z powodzeniem użyć w funkcjach WinAPI. W przypadku, gdy AnsiString
używany będzie jako stała wystarczy jedynie prosta konwersja PChar(AnsiString)
. Sprawa komplikuje się, gdy chcemy użyć AnsiString
jako bufor (np. w funkcji GetWindowsDirectory). Wtedy należy postępować podobnie jak w poniższym wydruku.
const
a : AnsiString = '\Temp\';
var
s : AnsiChar;
begin
SetLength(s, 260); //przedłużenie s tak by mogło przechować ścieżkę Windows będąc w postaci PChar
GetWindowsDirectory(PChar(s), 260);
SetLength(s, StrLen(PChar(s))); //zmiana długości z 260 znaków do ich rzeczywistej liczby
{
Zamiast funkcji SetLength można wykorzystać mechanizmy kompilatora.
Wystarczy rzutować zmienną AnsiString na PChar a następnie przypisać ją do zmiennej typu AnsiString:
s := PChar(s);
}
s := s + a; //teraz można z powodzeniem dokonać konkatenacji
end.
Najpierw przedłużamy łańcuch z zera do długości 260 znaków. Wtedy do funkcji przekazany zostanie PChar
o długości 260 znaków. Jeżeli krok ten zostanie pominięty do funkcji trafi PChar
o długości 0 znaków. Wartość nie będzie mogła przechować ścieżki Windows (nie będzie wystarczającej ilości pamięci na jej zapisanie).
Na typie, AnsiString
nie można dokonać konkatenacji, gdy rzeczywista długości różni się od tej, która jest zadeklarowana. To znaczy, gdy AnsiString
będzie miał długość, np. 10 znaków, a przechowywał będzie łańcuch np. Michał. Wynika to z ukrytej gospodarki zarządzania pamięcią związaną z AnsiString. W powyższym przykładzie wartość s ma długość 260 znaków, podczas, gdy w większości przypadków będzie przechowywać ciąg C:\Windows. Trzeba wtedy uaktualnić informację o długości. Robimy to przy pomocy SetLength
(patrz wydruk).
Rzutowanie w odwrotną stronę, czyli z PChar
na AnsiString
, jest równierz bardzo proste. Wystarczy przypisać zmienną typu PChar do zmiennej typu AnsiString.
Zobacz też:
Nie AnsiChar tylko AnsiString lub zwykły String!
Bo typ AnsiChar może zawierać TYLKO JEDEN ZNAK!!! Tak samo z Char.
Błąd:
[DCC Error] FtpUploader.pas(120): E2197 Constant object cannot be passed as var parameter
Ma ktoś jakiś pomysł jak to poprawić??