string、AnsiString、WideString

Delphi 的字符及字符串[1] - string、AnsiString、WideString、String[n]、ShortString

//最常用的 string

var
str: string;  {定义}
begin
str := '万一'; {赋值}
ShowMessage(IntToStr(Length(str))); {长度是: 4}
end;

//长字符串 AnsiString; 在当前版本(2007)中的默认状态下, String 就是 AnsiString

var
str: AnsiString;
begin
str := '万一';
ShowMessage(IntToStr (Length(str))); {长度是: 4}
end;

//宽字符串 WideString (效率不及 AnsiString)

var
str: WideString;
begin
str := '万一';
ShowMessage(IntToStr (Length(str))); {长度是: 2}
end;

//定长字符串

var str1: String[6]; {指定大小不能超过 255}
str2: String[100];
begin
{少给了也会占那些内存}
str1 := '万一';
ShowMessage(str1);          {万 一}
ShowMessage(IntToStr(Length(str1))); {4; 这是字符串的长度}
ShowMessage (IntToStr(SizeOf(str1))); {7; 这是占内存大小}
{多给了会被截断}
str1 := '万一的 Delphi 博客';
ShowMessage(str1);          {万一的}
ShowMessage(IntToStr (Length(str1))); {6; 这是实际保存的字符串长度}
ShowMessage(IntToStr(SizeOf(str1))); {7; 这是占内存大小}
{问题: 不是声明大小为 6 吗? 怎么 SizeOf 是 7 ? }
{因为定长字符串会 多出一个首字节来记忆字符串的实际长度}
{举例说明, 如果给 str2 按如下赋值, 那它的首字节 (sstr2[0])肯定储存着字符 'A'}
str2 := 'iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii'; {65个}
ShowMessage(str2[0]);        {A}
ShowMessage(IntToStr(Ord(str2[0]))); {65s; 这是 'A' 在 ASCII 序列中的序号, 用的就是它}
{那以后可以用 Ord(str2[0]) 来代替 Length 判 断字符串的长度吗? }
{对定长字符串是可以的; 不但能读取, 还可以像 SetLength 一样设置}
end;

//ShortString; 它相当于 String[255]

var
str: ShortString;
begin
str := '万一的 Delphi 博客';
ShowMessage(str);          {万一的 Delphi 博客}
ShowMessage(IntToStr(SizeOf (str))); {256; 这是大小}
ShowMessage(IntToStr(Length(str))); {18 ; 这是实际长度}
ShowMessage(IntToStr(Ord(str[0]))); {18 ; 这是从首字节中取出的长度}
end;

Delphi的字符及字符串[2] - Char、AnsiChar、WideChar、PChar、PAnsiChar、PWideChar

 

//单字符 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;
//宽字符指针 PWideCharvar
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;//
唉~: 代码着色在这里又出现问题, 不过现在没心情修改了, 以后再说吧.

//String 的指针地址及实际的内存地址
var
str: string;
pstr: PString;
pc: PChar;
begin
{在没有给 str 赋值以前, 既然声明了, 就有了指针地址 (@str):}
ShowMessage(IntToStr(Integer(@str))); {1244652; 这是在栈中的 str 的指针地址} 
{但现在还没有分配真正储存字符串内存}
ShowMessage(IntToStr(Integer(str))); {0; 0 就是 null}
str := 'Delphi';
{一旦赋值后...}
ShowMessage(IntToStr(Integer (@str))); {1244652; 这是在栈中的 str 的指针地址}
ShowMessage(IntToStr(Integer(str)));  {4580800; 这是在堆中的 str 的实际地址}
{通过指针地址获取字符串, 其中的 pstr 是前面定义 的字符串指针}
pstr := @str;
ShowMessage(pstr^); {Delphi}
{通过实际地址获取字 符串, 其中的 pc 是前面定义的字符指针}
pc := PChar(Integer(str));
ShowMessage(pc);   {Delphi}
end;
一个字符串(AnsiString 或 String, 譬如是 "Form1" )在内 存中是这样储存的:

 

                F o r m 1  

 

黄色区域是真正存字符串的位置, 前面说的字符串所在的内存地址, 就是本例中的 "F" 所 在的位置;

蓝色的四个字节储存一个 Integer 值, 表示字符串的长度;

最后红色的一个字节储存一个空字符(#0), 表示字符串的结束, 同时也是为了和 Windows 的 null 结 束的字符串兼容;

绿色的四个字节也是一个 Integer 值, 表示该字符串被引用的次数(也就是有几个字符串的指针指向 它).

还是看例子吧:var
str,s1,s2: string;
pint: PInteger;
begin
str := Self.Text; {把窗体标题给它吧; 现在 str 指向了窗体标题所在的内存位置}
s1 := str;     {给 s1 赋值}
s2 := str;    {给 s2 赋值; 现在窗体标题已经有了 str、s1、s2 三个引 用}
{str、s1、s2 的指针肯定不一样; 但现在指向内存的同一个位置, 测试:}
ShowMessage (IntToStr(Integer(str))); {15190384}
ShowMessage(IntToStr(Integer(s1))); {15190384}
ShowMessage(IntToStr(Integer(s2))); {15190384}
{向左偏移 4 个字节就是字符串长度的位 置, 读出它来(肯定是5):}
pint := PInteger(Integer(str) - 4);
ShowMessage(IntToStr (pint^));   {5}
{向左偏移 8 个字节就是字符串的引用计数, 读出它来(肯定是3):}
pint := PInteger(Integer(str) - 8);
ShowMessage(IntToStr(pint^));   {3}
end;
当某段字符串内存的引用计数为 0 时, Delphi 就会自动释放它; 这也是字符串不需要手 动释放的原因.

我在测试时发现: 所有常量和非全局的变量的引用计数一直是 "-1".

//字符串与字符数组var
arr: array[0..5] of Char;
str: string;
begin
{可以把字符串常量直接赋给字符数组; 但超界不行}
arr := 'Delphi';
ShowMessage(arr); {Delphi}
{可以把字符数组直接赋给字符串变量}
str := arr;
ShowMessage(str); {Delphi}
{但不能把一个字符串变量赋给字符数组}
//arr := str;    {错误}
{其实字符串内部也是包含了一个字符数组, 所以能索引访问, 不过它的索引起始于 1} 
ShowMessage(str[1]); {D}
ShowMessage(arr[0]); {D}
end;

//字符指针与字符数组

var
arr: array[0..6] of Char;
p: PChar;
begin
arr := 'Delphi';
{如果直接把字符数组给字符指针, 结果不保险, 因为字符指针要找空字符(#0)结束}
{把数组的最后一个元素给 #0 就可以了}
arr[Length(arr)-1] := #0;
p := arr;
ShowMessage(p); {Delphi}
{假如把 #0 给到中间会怎样?}
arr[3] := #0;
p := arr;
ShowMessage(p); {Del; 给截断了}
end;


//字符串常量 > 字符数

组常量 
const
arr1: array[0..5] of Char = 'Delphi';
arr2: array[0..5] of AnsiChar = AnsiString('Delphi');
begin
ShowMessage(arr1[0]); {D}
ShowMessage (arr2[0]); {D}
end;


Windows API 中的字符串对应这 Delphi 的 PChar(PAnsiChar); 在 API 中使用 Delphi 的字符串还 是比较灵活的.
先说赋值://赋值方法1: 给直接量
begin
 SetWindowText(Handle, '新标题');
end;
//赋值方法2: 定义它要的类型
var
 p: PChar;
begin
 p := '新标题';
 SetWindowText(Handle, p);
end;
//赋值方法3: 转换成它要的类型
var
 str: string;
begin
 str := '新标题';
 SetWindowText(Handle, PChar(str));
end;
//赋值方法4: 用字符数组
var
 arr: array[0..255] of Char;
begin
 arr := '新标题';
 SetWindowText(Handle, arr);
end;
再说取值://取值方法1: 用字符数组(经常被称作"缓冲区")
var
 arr: array[0..254] of Char;
begin
 GetWindowText(Handle, arr, 255);
 ShowMessage(arr); {Form1}
end;
//取值方法2: 使用 GetMem 给 PChar 分配内存
var
 p: PChar;
begin
 GetMem(p, 255); {分配内存}
 GetWindowText(Handle, p, 255);
 ShowMessage(p); {Form1}
 FreeMem(p);   {释放内存}
end;
//取值方法3: 用 GlobalAlloc 分配全局内存(比 GetMem 慢)
var
 p: HGLOBAL;
begin
 p := GlobalAlloc(0, 255); {参数一给 0 或 GMEM_FIXED 表示分配的是固定内存}
 GetWindowText(Handle, PChar(p), 255);
 ShowMessage(PChar(p)); {Form1}
 GlobalFree(p);     {释放内存}
end;
//取值方法4: 直接使用 string; 需要先 SetLength, 然后再去除空白:
var
 str: string;
begin
 SetLength(str, 255); {先设定 str 的长度}
 GetWindowText(Handle, PChar(str), 255);
 {但此时 str 的长度是 255 啊!}
 str := PChar(str); {这样可以得到实际长度的字符串}
 ShowMessage(str); {Form1}
end;
定长字符串不是 #0 结束的, 和 API 不好兼容, 一般不用于 API 中. c
Delphi的字符及字符串[6] - Char(AnsiChar)、WideChar与其编码的相互转换
//Char 类型与其编码值的转换:
var
 b: Byte;
 c: Char;
begin
 b := Ord('A');  {返回: 65}
 b := Ord(#65);  {返回: 65}
 b := Ord($41);  {返回: 65}
 b := Ord(#$41); {返回: 65}
 b := Byte('A'); {返回: 65}
 b := Byte(#65); {返回: 65}
 b := Byte($41); {返回: 65}
 b := Byte(#$41); {返回: 65}
 c := Chr(65);  {返回: A }
 c := Chr($41);  {返回: A }
 c := Char(65);  {返回: A }
 c := Char($41); {返回: A }
end;

//WideChar 类型与其编码值的转换; 汉字的 UniCode 编码范围是: $4E00..$9FA5
var
 w : Word;
 c : WideChar;
 ws: WideString;
 s : string;
begin
 {准备工作}
 ws := '万一';
 c := ws[1];
//ShowMessage(c); {万}
 {从汉字到 UniCode 编码}
 w := Ord(c);         {返回十进制数    : 19975}
 w := Word(c);         {返回十进制数    : 19975}
 s := Format('%.4x',[Ord(c)]); {返回十六进制的字符串: 4E07 }
 s := IntToHex(Ord(c), 4);   {返回十六进制的字符串: 4E07 }
 {从 UniCode 编码到汉字}
 c := #19975;      {万}
 c := #$4E07;      {万}
 c := #$4e07;      {万}
 c := WideChar(19975); {万}
 c := WideChar($4E07); {万}
end;


posted @ 2014-07-08 02:49  Wishmeluck  阅读(363)  评论(0编辑  收藏  举报