字符串類型
Delphi有三種類型的字符:
●AnsiChar這是標准的1字節的ANSI字符,程序員都對它比較熟悉。
●WideChar這是2字節的Unicode字符。
●Char在目前Delphi早前的版本相當於AnsiChar,但在Delphi以后版本中相當於WideChar.
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 個字節}
{如果不超出 #255 的范圍是可以直接賦值的}
c := 'B';
ShowMessage(c); {萬}
end;
記住因為一個字符在長度上並不表示一個字節,所以不能在應用程序中對字符長度進行硬編碼,而應該使用Sizeof()函數。注意Sizeof()標准函數返回類型或實例的字節長度。
字符串是代表一組字符的變量類型,Delphi有下列幾種不同的字符串類型來滿足程序的要求:
●AnsiString這是Pascal缺省的字符串類型(string),它由AnsiChar字符組成,其長度沒有限制,同時與null結束的字符串相兼容。
●ShortString保留該類型是為了向后兼容Delphi1.0,它的長度限制在255個字符內。
●WideString功能上類似於AnsiString,但它是由WideChar字符組成的。
●PChar指向null結束的Char字符串的指針,類似於C的char*或lpstr類型。
●PAnsiChar指向null結束的AnsiChar字符串的指針。
●PWideChar指向null結束的WideChar字符串的指針。
1.AnsiString類型
AnsiString(或長字符串)類型是在Delphi2.0開始引入的,因為Delphi1.0的用戶特別需要一個容易使用而且沒有255個字符限制的字符串類型,而AnsiString正好能滿足這些要求。
雖然AnsiString在外表上跟以前的字符串類型幾乎相同,但它是動態分配的並有自動回收功能,正是因為這個功能AnsiString有時被稱為生存期自管理類型。ObjectPascal能根據需要為字符串分配空間,所以不用像在C/C++中所擔心的為中間結果分配緩沖區。實際上,AnsiString類型是一個指向在堆棧中的字符串結構的指針。
AnsiString字符串類型有引用計數的功能,這表示幾個字符串都能指向相同的物理地址。因此,復制字符串因為僅僅是復制了指針而不是復制實際的字符串而變得非常快。當兩個或更多的AnsiString類型共享一個指向相同物理地址的引用時,Delphi內存管理使用了copy-on-write技術,一個字符串要等到修改結束,才釋放一個引用並分配一個物理字符串。當我們聲明一個AnsiString變量時,它是沒有長度的,因此字符串沒有為字符申請內存空間。為了申請字符串的內存空間,我們可以給它賦值為一行字符或者另一個字符串變量,也可以使用SetLength()函數。
var
str: string;
s: string;
begin
str := 'delphi';
s[1] := 'd';//錯誤,s此時還沒有申請內存空間。
s := 'delphi'; //或者s := str; 或者SetLength(s, 4);
end;
AnsiString字符串總是null結束的,這就使得調用Win32API函數或其他需要PChar型字符串的函數變得容易了。只要把一個字符類型強制轉換為PChar類型。我們在調用時進行強制類型轉換PChar(s),或者@s[1]。(s[1]是字符串s的第一個字符,@是取地址。s[0]存放的是字符串s的長度)。下面的代碼演示了怎樣調用Win32的GetWindowsDirectory()函數,這個函數需要一個PChar類型的參數:
var
S:String;
begin
SetLength(S,256);//重要!首先給字符串分配空間
//調用API函數,S現在包含目錄字符串
GetWindowsDirectory(PChar(S),256);
如果使用了將AnsiString字符串強制轉換為PChar類型的函數和過程,在使用結束后,要手工把它的長度恢復為原來以null結束的長度。STRUTILS單元中的RealizeLenght()函數可以實現這一點:
procedure RealizeLength(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;
2. ShortString類型
ShortString類型跟以null結尾的字符串不兼容,正因為這樣,用ShortString調用Win32函數時,要做一些工作。下面這個ShortStringAsPChar()函數是在STRUTILS.PAS單元中定義的。
function ShortStringAsPChar(varS:ShortString): PChar;
{這函數能使一個字符串以null結尾,這樣就能傳遞給需要PChar類型參數的Win32API函數,如果字符串超過254個字符,多出的部分將被截掉}
begin
if Length(S)=High(S) then Dec(S[0]); {如果S太長,就截取一部分}
S[Ord(Length(S))+1]:=#0;{把null加到字符串的最后}
Result:=@S[1];{返回PChar化的字符串}
end;
Win32API函數需要以null結尾的字符串,不要把ShortString字符串傳遞給API函數,因為編譯器將報錯,長字符串可以傳遞給Win32API函數。
3.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中一個特定的字符。
4.字符串指針
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類型以及對它相應進行人工分配內存。在Debug下開啟內存泄漏:
{$ifdef DEBUG}
ReportMemoryLeaksOnShutDown := True; //插入到應用程序入口的begin語句下。
{$endif}
通常,用StrAlloc()函數為PChar緩沖區分配內存,但是其他幾種函數也能用來為PChar類型分配函數,包括AllocMem()、GetMem()、StrNew()和VirtualAlloc()API函數。這些函數有相應的釋放內存的函數。
var
p: PChar;
str: string;
begin
{可以給 PChar 直接賦予字符串常量}
p := '中國';
ShowMessage(IntToStr(Length(p))); {4}
{給變量值需要轉換}
str := '中國杭州';
p := PChar(str); {轉換}
ShowMessage(p); {中國杭州}
ShowMessage(IntToStr(Length(p))); {8}
end;
//寬字符指針 PWideChar
var
p: PWideChar;
str: WideString; {注意這里不是 String}
begin
{可以給 PWideChar 直接賦予字符串常量}
p := '中國';
ShowMessage(IntToStr(Length(p))); {2}
{給變量值需要轉換}
str := '中國杭州';
p := PWideChar(str); {轉換}
ShowMessage(p); {中國杭州}
ShowMessage(IntToStr(Length(p))); {4}
end;
數值型
整數型(Integer):整數型是整數的集合,包括多種不同的整數類別。
類型 |
表示范圍 |
字節 |
ShortInt |
-128~127 |
8位,有正負符號標志 |
SmallInt |
-32 768~32 767 |
16位,有正負符號標志 |
LongInt |
-2 147 443 648~2 147 483 647 |
32位,有正負符號標志 |
Integer |
-2 147 443 648~2 147 483 647 |
32位,有正負符號標志 |
Int64 |
-263~263-1 |
64位,有正負符號標志 |
Byte |
0~255 |
8位,無正負符號標志 |
Word |
0~65 535 |
16位,無正負符號標志 |
LongWord |
0~4 294 967 295 |
32位,無正負符號標志 |
Cardinal |
0~4 294 967 295 |
32位,無正負符號標志 |
實數型(Real):實數型是實數的集合,包括多種不同的類型。Real類型只有在和以前Borland Pascal兼容的情況下才使用,否則應使用Double或Extended類型。
類型 |
范圍 |
有效位 |
存儲字節 |
Real48 |
2.9×10-39 ~ 1.7×1038 |
11~12 |
6 |
Single |
1.5×10-45 ~ 3.4×1038 |
7~8 |
4 |
Double |
5.0×10-324 ~ 1.7×10308 |
15~16 |
8 |
Extended |
3.6×10-4951 ~ 1.1×104932 |
19~20 |
10 |
Comp |
-263+1 ~ 263-1 |
19~20 |
8 |
Currency |
-922 337 203 685 477.5 808 ~ 922 337 203 685 477.5 807 |
19~20 |
8 |
Real |
5.0×10-324 ~ 1.7×10308 |
15~16 |
8 |
Delphi數據類型分類如下:
分類 |
范圍 |
字節 |
備注 |
|||
簡單類型 |
序數 |
整數 |
Integer |
-2147483648 .. 2147483647 |
4 |
有符號32位 |
Cardinal |
0 .. 4294967295 |
4 |
無符號32位 |
|||
Shortint |
-128 .. 127 |
1 |
有符號8位 |
|||
Smallint |
-32768 .. 32767 |
2 |
有符號16位 |
|||
Longint |
-2147483648 .. 2147483647 |
4 |
有符號32位 |
|||
Int64 |
-263 .. 263 |
8 |
有符號64位 |
|||
Byte |
0 .. 255 |
1 |
無符號8位 |
|||
Word |
0 .. 65535 |
2 |
無符號16位 |
|||
Longword |
0 .. 4294967295 |
4 |
無符號32位 |
|||
字符 |
AnsiChar(Char) |
ANSI字符集 |
8位 |
|||
WideChar |
Unicode字符集 |
16位 |
||||
布爾 |
Boolean |
False < True |
1 |
|||
ByteBool |
False <> True |
1 |
||||
WordBool |
2 |
|||||
LongBool |
4 |
|||||
枚舉 |
||||||
子界 |
||||||
實數 |
|
Real |
5.0×10-324 .. 1.7×10308 |
8 |
[精度]15..16 |
|
Real48 |
2.9×10-39 .. 1.7×1038 |
6 |
[精度]11..12; |
|||
Single |
1.5×10-45 .. 3.4×1038 |
4 |
[精度]7..8 |
|||
Double |
5.0×10-324 .. 1.7×10308 |
8 |
[精度]15..16 |
|||
Extended |
3.6×10-4951 .. 1.1×104932 |
10 |
[精度]19..20 |
|||
Comp |
-263 + 1 .. 263 - 1 |
8 |
[精度]19..20 |
|||
Currency |
-922337203685477.5808 .. |
8 |
[精度]19..20 |
|||
字符串 |
|
|
ShortString |
255個字符 |
2..256B |
向后兼容 |
AnsiString |
大約 231 個字符 |
4B..2GB |
8位(ANSI)字符 |
|||
WideString |
大約 230 個字符 |
4B..2GB |
多用戶服務和 |
|||
其他 |
String |
|||||
結構類型 |
集合 |
|
Set |
最多256個元素[0..255] |
||
數組 |
靜態數組 |
|
|
|||
動態數組 |
|
|||||
記錄 |
|
Record |
||||
文件 |
|
File |
||||
類 |
|
Class |
||||
類引用 |
|
Class reference |
||||
接口 |
|
Interface |
||||
指針類型 |
無類型指針 |
|
Pointer |
|||
有類型指針 |
預定義類型指針 |
PAnsiString |
||||
過程類型 |
程序過程類型 |
|
Procedural |
|||
對象過程類型 |
|
Procedural |
|
|||
變體類型 |
|
|
Variant |
|
||
|
OleVariant |
|
強制類型轉換
一、數的類型轉換
把表達式的類型從一種類型轉化為另一種類型,結果值是把原始值截斷或擴展,符號位保持不變。例如:
數的類型轉換
字符轉換為整數 Integer('A')
整數轉換為字符 Char(48)
整數轉換為1個字節的邏輯型 Boolean(0)
整數轉換為2個字節的邏輯型 WordBool(0)
整數轉換為4個字節的邏輯型 LongBool(0)
整數轉換為10進制pascal型字符串 caption:=intToStr(15)
整數轉換為16進制pascal型4位字符串 caption:=intToHex(15,4)
地址轉換為長整型數 Longint(@Buffer)
二、數的"分開"與"合成"
取32位longint型數的 高16位數為 hiword(longint-var)
低16位數為 loword(longint-var)
取16位數的 高8位數為 hibyte(integer_var)
低8位數為 lobyte(integer_var)
取32位地址的段選擇符和偏移量 段選擇符(高16位地址)為 selectorof(p)
偏移量(低16位地址)為 offsetof(p)
段選擇符和偏移量合成為指針 Ptr(SEG, OFS: Word)相當於C語言的宏MK-FP(SEG,OFS)
例如在Windows中,Task DataBase結構0FAh偏移處包含'TD'標識,我們可以容易地編寫如下代碼觀察到這個位於Windows內部的未公開的秘密:
{函數ptr(seg,ofs)的用法,相當於C語言的MK-FP(seg,ofs)}
var p:pbyte;ch:char;
p:=ptr(getcurrentTask,$FA);
ch:=char(p^); {結果為ch='T'}
p:=ptr(getcurrentTask,$FA+1);
ch:=char(p^); {結果為ch='D'}
三、字符串string 字符數組與指向字符串的指針pchar的區別與聯系
1、使用指向字符串的指針,如果不是以0結尾,運行時就會出現錯誤。為了避免這種錯誤,需要在字符串結尾人工加入0 即char(0),或用strpcopy函數在字符串結尾自動加0。
例1: 指向字符串的指針,如果不是以0結尾,運行時會出現錯誤:
{s[0]=3 s[1]='n' s[2]='e' s[3]='w'}
var
s:string;
p:pchar;
begin
s:='new';
label1.caption:=s; {new}
label2.caption:=intTostr(integer(s[0]));{3是字符串的長度}
p:=@s[1];{不是以0結尾,莫用pchar型指針}
label3.caption:=strpas(p); {運行時出現錯誤}
end;
例2:在字符串結尾人工加入0即char(0),可使用指向字符串的指針。
{s[0]=4 s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
{p-->'new'}
var
s:string;
p:pchar;
begin
p:=@s[1];
s:='new'+char(0); {以0結尾,可用pchar型指針}
label1.caption:=strpas(p); {new}
label2.caption:=s; {new}
label3.caption:=intTostr(integer(s[0])); {4是字符串長度}
end;
例3: 用strpcopy函數賦值會在字符串s結尾自動加0。
{s[0]=4 s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
{p-->'new'}
var
s:string;
p:pchar;
begin
p:=@s[1];
strpcopy(p,'new');{strpcopy函數在字符串結尾自動加0}
label1.caption:=strpas(p);{new}
label2.caption:=s;{new}
label3.caption:=intTostr(integer(s[0]));{4}
end;
2、下標為0的字符串標識符存放的是字符串長度,字符型數組基本相當於字符串,但不能存放字符串長度。字符串可以用s:='a string'的形式賦值,但是字符型數組a[ ]不可直接用a:='array'的形式賦值,用此種形式會出現類型不匹配錯誤,可選用strpcopy函數賦值。
例4: 字符型數組s[ ]相當於字符串,但沒有存放字符串長度的位置。
{s[1]='n' s[2]='e' s[3]='w' s[4]=0;}
{p-->'new'}
var
s:array[1..10] of char;
p:pchar;
begin
{s:='new'+char(0); error}{錯誤}
p:=@s[1];
{p:=@s; is not correct}
strpcopy(p,'new');
label1.caption:=strpas(p);{new}
label2.caption:=s;{new}
{label3.caption:=intTostr(integer(s[0]));}{不會出現4, 下標超出錯誤}
end;
例5:下標從0開始的字符數組s,s相當於@s[0]。
{ s[0]='n' s[1]='e' s[2]='w' s[3]=0;}{p-->'new'}
var
s:array[1..10] of char;
p:pchar;
begin
{s:='new'+char(0); error}{錯誤}
p:=s;
{p:=@s[0] is also correct}
strpcopy(p,'new');
label1.caption:=strpas(p);{new}
label2.caption:=s;{new}
label3.caption:=s[0];{n}
end;
3、下標從0開始和從1開始的字符數組地址的表示方法也有細微不同:
例6:下標從1開始的數組a 與下標從0開始的數組b 的比較。
var
a:array[1..10]of char;
b:array[0..10]of char;
{a:='1..10';}{type mismatch}
{b:='0..10';}{type mismatch}
begin
strpcopy( b, 'from 0 to 10'); {正確 因為b即是@b[0] }
strpcopy(@b[0], 'from 0 to 10'); {正確 與上個表達式結果相同}
strpcopy(@a[1], 'from 1 to 10'); {正確 }
strpcopy( a, 'from 1 to 10'); {類型匹配錯誤 因為a即是@a[0]}
end;