Delphi有三種類型的字符:
AnsiChar這是標准的1字節的ANSI字符,程序員都對它比較熟悉。
WideChar這是2字節的Unicode字符。
Char在目前相當於AnsiChar,但在Delphi 2010 以后版本中相當於WideChar.
記住因為一個字符在長度上並不表示一個字節,所以不能在應用程序中對字符長度進行硬編碼,
而應該使用Sizeof()函數。注意Sizeof()標准函數返回類型或實例的字節長度。
Delphi有下列幾種不同的字符串類型 String:
ShortString保留該類型是為了向后兼容 Delphi1.0,它的長度限制在255個字符內。
ShortString[0] = len : $H- 代表 ShortString
AnsiString是Pascal缺省的字符串類型,它由AnsiChar字符組成,其長度沒有限制,
同時與null結束的字符串相兼容。<Delphi2.0開始引入> : $H+ 代表 AnsiString
WideString功能上類似於AnsiString,但它是由WideChar字符組成的。
WideString沒有引用計數,所以將一個WideString字符串賦值給另一個WideString字符串時,
就需要從內存中的一個位置復制到另一個位置。這使得WideString在速度和內存的利用上不如AnsiString有效。
缺省情況下,編譯器認為是AnsiString字符串< <Delphi2010 String 默認為 WideString>
Delphi有下列幾種不同的字符串指針類型:
PChar指向null結束的Char字符串的指針,類似於C的char*或lpstr類型。
PAnsiChar指向null結束的AnsiChar字符串的指針。
PWideChar指向null結束的WideChar字符串的指針。<Delphi2010 PChar 默認為 PWideChar>
字符數組
靜態字符數組 CharArray : array[0..Length-1] of Char;
動態字符數組 CharArray : array of Char;
動態數組的構造和靜態數組完全不同, 它和 String 的構造相似
String : RefCount : Length : Char Char .... 0x00 < Delphi >
DynArry : RefCount : Length : Char Char .... 0x00 < Programmer >
StaArry : : Char Char .... 0x00 < Programmer >
: PChar = PChar( String ) = PChar( DynArray ) = PChar( StaArray )
StrLen( PChar ) : 字符個數, StrLen( PChar ) * SizeOf( Char ) : 字節個數 ( WideChar = 2 )
Length( String ) : 字符個數, Length( PChar ) * SizeOf( Char ) : 字節個數 ( AnsiChar = 1 )
1.AnsiString類型
AnsiString(或長字符串)類型是在Delphi2.0開始引入的,因為Delphi1.0的用戶特別需要一個容易使用而且沒有255個字符限制的字符串類型,而AnsiString正好能滿足這些要求。
雖然AnsiString在外表上跟以前的字符串類型幾乎相同,但它是動態分配的並有自動回收功能,正是因為這個功能AnsiString有時被稱為生存期自管理類型。ObjectPascal能根據需要為字符串分配空間,所以不用像在C/C++中所擔心的為中間結果分配緩沖區。另外,AnsiString字符串總是以null字符結束的,這使得AnsiString字符串能與Win32API中的字符串兼容。實際上,AnsiString類型是一個指向在堆棧中的字符串結構的指針。
AnsiString字符串類型有引用計數的功能,這表示幾個字符串都能指向相同的物理地址。因此,復制字符串因為僅僅是復制了指針而不是復制實際的字符串而變得非常快。當兩個或更多的AnsiString類型共享一個指向相同物理地址的引用時,Delphi內存管理使用了copy-on-write技術,一個字符串要等到修改結束,才釋放一個引用並分配一個物理字符串。下面的例子顯示了這些概念:
var
S1,S2:string;
begin
//給S1賦值,S1的引用計數為1
S1:='Andnowforsomething...';
S2:=S1;//現在S2與S1指向同一個字符串,S1的引用計數為2
//S2現在改變了,所以它被復制到自己的物理空間,並且S1的引用計數減1
S2:=S2+'completelydifferent1';
end;
Win32的兼容
正如前面所提到,AnsiString字符串總是null結束的。因此,它能跟以null結尾的字符串兼容,這就使得調用Win32API函數或其他需要PChar型字符串的函數變得容易了。只要把一個字符類型強制轉換為PChar類型(在2.8節“強制類型轉換和類型約定”中將介紹強制類型轉換)。下面的代碼演示了怎樣調用Win32的GetWindowsDirectory()函數,這個函數需要一個PChar類型的參數:
var
S:String;
begin
SetLength(S,256);//重要!首先給字符串分配空間
//調用API函數,S現在包含目錄字符串
GetWindowsDirectory(PChar(S),256);
如果使用了將AnsiString字符串強制轉換為PChar類型的函數和過程,在使用結束后,要手工把它的長度恢復為原來以null結束的長度。STRUTILS單元中的RealizeLenght()函數可以實現這一點:
procedureRealizeLength(varS:string);
begin
SetLength(S,StrLen(PChar(S)));
end;
調用ReallizeLength():
var
S:string;
begin
SetLength(S,256);//重要!首先給字符串分配空間
//調用函數,S現在包含目錄字符串
GetWindowDirectory(PChar(S),256);
RealizeLength(S);//設置S的長度為null結束的長度
end;
跟AnsiString類型字符串不一樣,ShortString跟以null結尾的字符串不兼容,正因為這樣,用ShortString調用Win32函數時,要做一些工作。下面這個ShortStringAsPChar()函數是在STRUTILS.PAS單元中定義的。
funcfunctionShortStringAsPChar(varS:ShortString):PChar;
{這函數能使一個字符串以null結尾,這樣就能傳遞給需要PChar類型參數的Win32API函數,如果字符串超過254個字符,多出的部分將被截掉}
begin
ifLength(S)=High(S)thenDec(S[0]);{如果S太長,就截取一部分}
S[Ord(Length(S))+1]:=#0;{把null加到字符串的最后}
Result:=@S[1];{返回PChar化的字符串}
end;
Win32API函數需要以null結尾的字符串,不要把ShortString字符串傳遞給API函數,因為編譯器將報錯,長字符串可以傳遞給Win32API函數。
WideString類型
WideString類型像AnsiString一樣是生存期自管理類型,它們都能動態分配、自動回收並且彼此能相互兼容,不過WideString和AnsiString的不同主要在三個方面:
WideString由WideChar字符組成,而不是由AnsiChar字符組成的,它們跟Unicode字符串兼容。
WideString用SysAllocStrLen()API函數進行分配,它們跟OLE的BSTR字符串相兼容。
WideString沒有引用計數,所以將一個WideString字符串賦值給另一個WideString字符串時,就需要從內存中的一個位置復制到另一個位置。這使得WideString在速度和內存的利用上不如AnsiString有效。
就像上面所提到的,編譯器自動在AnsiString類型和WideString類型的變量間進行轉換。示例如下:
var
W:wideString;
S:string;
begin
W:='Margaritaville';
S:=W;//wideString轉換成AnsiString
S:='ComeMonday';
W:=S;//AnsiString轉換成WideString
end;
為了能靈活地運用WideString類型,ObjectPascal重載了Concat()、Copy、Insert()、Length()、Pos()和SetLength()等例程以及+、=和<>等運算符。
就像AnsiString和ShortString類型一樣,能用數組的下標來訪問WideString中一個特定的字符:
var
W:WideString;
C:WideChar;
begin
W:='EbonyandIvorylivinginprefectharmony';
C:=W[Length(W)];//C包含W字符串的最后一個字符
end;
以null結束的字符串
正如前面所提到的,Delphi有三種不同的以null結束的字符串類型:PChar、PAnsiChar和PWideChar。它們都是由Delphi的三種不同字符組成的。這三種類型在總體上跟PChar是一致的。PChar之所以保留是為了跟Delphi1.0和Win32API兼容,而它們需要使用以null結束的字符串,PChar被定義成一個指向以null(零)結束的字符串指針與AnsiString和WideString類型不同,PChar的內存不是由ObjectPascal自動產生和管理的,要用Object Pascal的內存管理函數來為PChar所指向的內存進行分配。PChar字符串的理論最大長度是4GB
在大多數情況下,AnsiString類型能被用成PChar,應該盡可能地使用AnsiString,因為它對字符串內存的管理是自動,極大地減少了應用程序中內存混亂的錯誤代碼,因此,要盡可能地避免用PChar類型以及對它相應進行人工分配內存。
正如在前面所提到的,PChar變量需要人工分配和釋放存放字符串的內存。通常,用StrAlloc()函數為PChar緩沖區分配內存,但是其他幾種函數也能用來為PChar類型分配函數,包括AllocMem()、GetMem()、StrNew()和VirtualAlloc()API函數。這些函數有相應的釋放內存的函數。
--------------------------------------------------------------------------------
//單字符 Char、AnsiChar (在目前版本(2007)中, 它們是一回事, 只有 1 字節大小)
var
c: Char; {Char 類型的取值范圍是: #0..#255, 用十六進制表示是: #$0..#$FF}
begin
{用十進制方式賦值:}
c := #65;
ShowMessage(c); {A}
{用十六進制方式賦值:}
c := #$41;
ShowMessage(c); {A}
{用 Chr 函數代替 # 符號}
c := Chr(65);
ShowMessage(c); {A}
c := Chr($41);
ShowMessage(c); {A}
{Char 長度當然會是 1}
ShowMessage(IntToStr(Length(c))); {1}
{Char、AnsiChar 允許這樣方便地賦值(也就是和 1 字節長度的字符串是兼容的):}
c := 'B';
ShowMessage(c); {B}
end;
--------------------------------------------------------------------------------
//UniCode 字符 WideChar; 和 AnsiChar 不同, WideChar 是占 2 字節大小.
var
c: WideChar; {WideChar 的取值范圍是: #0..#65535, 用十六進制表示是: #$0..#$FFFF}
begin
{WideChar 兼容了 AnsiChar 的 #0..#255; 但占用了 2 字節大小}
c := #65;
ShowMessage(c); {A}
ShowMessage(IntToStr(Length(c))); {1; 這是字符長度}
ShowMessage(IntToStr(SizeOf(c))); {2; 但占用 2 個字節}
{用十六進制賦值}
c := #$4E07;
ShowMessage(c); {萬}
ShowMessage(IntToStr(Length(c))); {1; 這是字符長度}
ShowMessage(IntToStr(SizeOf(c))); {2; 但占用 2 個字節}
{用十進制賦值}
c := #19975;
ShowMessage(c); {萬}
{如果不超出 #255 的范圍是可以直接賦值的}
c := 'B';
ShowMessage(c); {萬}
{這樣不行}
//c := '萬'; {這是 Delphi 的支持問題, 估計 Delphi 2008 應該可以解決}
{可以這樣變通一下:}
c := WideString('萬')[1];
ShowMessage(c); {萬}
{用 WideChar 的方式顯示我的名字}
ShowMessage(#19975#19968); {萬一}
ShowMessage(#19975 + #19968); {萬一}
ShowMessage(#$4e07#$4e00); {萬一}
end;
--------------------------------------------------------------------------------
//字符指針 PChar、PAnsiChar; 在當前版本(2007)中它們沒有區別.
var
p: PChar;
str: string;
begin
{可以給 PChar 直接賦予字符串常量}
p := '萬一';
ShowMessage(p); {萬一}
ShowMessage(IntToStr(Length(p))); {4}
{給變量值需要轉換}
str := '萬一的 Delphi 博客';
p := PChar(str); {轉換}
ShowMessage(p); {萬一的 Delphi 博客}
ShowMessage(IntToStr(Length(p))); {18}
end;
--------------------------------------------------------------------------------
//寬字符指針 PWideChar
var
p: PWideChar;
str: WideString; {注意這里不是 String}
begin
{可以給 PWideChar 直接賦予字符串常量}
p := '萬一';
ShowMessage(p); {萬一}
ShowMessage(IntToStr(Length(p))); {2}
{給變量值需要轉換}
str := '萬一的 Delphi 博客';
p := PWideChar(str); {轉換}
ShowMessage(p); {萬一的 Delphi 博客}
ShowMessage(IntToStr(Length(p))); {13}
end;
詳見:http://www.haogongju.net/art/1213487
http://www.haogongju.net/art/1529020
http://www.haogongju.net/art/1994071