字符串

字符串

字符串类型种类

类型名 最大长度(字节) 所需内存 说明
ShortString 255 2~256 字节 单字节字符组成的字符串(短)
AnsiString 2GB 4字节~2GB 单字节字符组成的字符串
WideString 2GB 4字节~2GB 宽字节字符组成的字符串
UnicodeString 2GB 4字节~2GB Unicode 字符组成的字符串
  • 旧版本 Delphi 中,string 等价于 AnsiString
  • 新版本 Delphi 中,string 等价于 UnicodeString

WideString 与 UnicodeString 基本无区别。

ShortString

ShortString 为短字符串,最大长度 255 字节的单字符字符串。相当于 array[0..255] of byte

短字符串通过索引去取字符时,应该从 1 算起,例如 str[1] 表示取首个字符,因为数组的 str[0] 表示字符串的实际长度。

  • 可以通过 Length 函数获取短字符串的实际长度
  • 也可以通过 str[0] 取数组首字节获取短字符串的实际长度

长字符串

长字符串 AnsiStringWideStringUnicodeString 都是位于 Heap 内存区。

AnsiStringUnicodeString 长字符串自带引用计数的额外信息,由运行时自动托管,当引用计数为0时,会自动释放内存。

AnsiString

AnsiString 由编译器在前面插入12字节(32位时)/24字节(64位时)的额外信息。

额外信息包括(1)代码页(2)字符的大小(3)引用计数(4)字符串长度

AnsiString 实际上是一个指针,真正的内容放在堆中,指向字符串的内容。实际上额外信息放在这个指针负索引的位置。

获取首字符请使用 AnsiString[1],因为 AnsiString[0] 还存放了字符长度,AnsiString[0] 不可获取。Low(AnsiString) 永远是 1。

额外信息:

32位下的指针偏移 64位下的指针偏移 字段说明
-12 -24 代码页(2字节)
-10 -22 字符大小(2字节)
-8 -16 引用计数
-4 -8 字符串实际大小
0 0 字符串内容

UnicodeString

UnicodeString 是各字符为 UTF-16 的字符串。用法跟 AnsiString 差不多。也带引用计数,由运行时自动管理。

额外信息:

32位下的指针偏移 64位下的指针偏移 字段说明
-8 -16 引用计数
-4 -8 字符串实际大小
0 0 字符串内容

StringUnicodeString 的别名。在旧版中,StringAnsiString 的别名。

WideString

跟 UnicodeString 差不多,但是没有引用计数,一般用于 COM 编程以兼容 BSTR。

UTF8String

UTF8String 其实是一种 AnsiString,请看下面声明方式,就能理解:

1
2
Type
UTF8String = type AnsiString(CP_UTF8);

UCS4String

UCS4String 是 UTF-32 字符的字符串,实现跟 UnicodeString 一样,只是每个字符是 4 字节。

旧版本的 UCS4String 只是声明为 array of UTF32Char ,新版本的 Delphi 是跟 UnicodeString 一样有各个方法成员和引用计数等。

NULL 结尾

C 语言的字符串是以 NULL 结尾的。 与 C 语言的 API交互,Delphi 中的兼容用法有:

  • 利用索引从 0 开始的字符的数组来代替
  • 采用字符的指针 PChar 或者 PAnsiChar, PWideChar 来代替

Delphi 会默认在字符串后面加 ‘\0’ 以兼容 C 。

字符指针

需要与 C API交互的话,对字符串指针的初始化可以直接使用字符数组或者字符串类型转换成字符指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var 
arr1 : array[0..100] of char;
str1 : string;
str2 : ShortString;
p : PChar;
//
begin
str1 := '444';
//
p := arr1; // 字符数组可以直接赋值给字符指针 (OK)
p := @str1; // 这种取地址是得不到C字符串的,只能得到字符串变量的地址(指针的指针)
p := @str2; // 这种取地址是得不到C字符串的,只能得到字符串变量的地址(指针的指针)
p := 'hello'; // 字符串字面值常量可以直接赋值给字符指针 (OK)
p := PChar(str1); // 字符串可以类型转换为字符指针 (OK)
//
p[2] := '4'; // 可以通过p更改字符串里面的字符(字符指针索引从0开始)
end;
  • 可以通过 PChar()PWideChar()PAnsiChar() 强制类型转换获得跟 C 兼容的字符串指针,NULL字符结尾。
  • 如果字符串为空字符串,则得到的指针为 nil
  • Pointer(str) 强制类型转换可以得到无类型的指针。如果字符串为空字符串,同样得到的指针为 nil

注意:

  1. 通过 PChar() 等类型转换成字符指针 p 之后,可以通过 p 来更改字符串里面的字符
  2. 通过 PChar() 等类型转换成字符指针 p 之后,如果字符串变量被赋值更改,p 还是会指向旧字符串的原地址。

字符串、PChar与字符数组之间的转换

1
2
3
4
var
s: string;
p: pchar;
a: array[0..80] of char;

1. 字符串到PChar

1
2
// 字符串到 PChar
p := PChar(s);

2. PChar 到字符串

1
2
// PChar 到字符串
s := p;

3. PChar 到字符数组

1
2
// PChar 到字符数组
StrCopy(@a, p);

4. 字符数组到 PChar

1
2
// 字符数组到`PChar`
p = PChar(@a);

5. 字符串到字符数组

字符串与字符数组之间的转换就只有通过 PChar 来中转

1
2
StrCopy(@arr1, PChar(str1));
str2 := PChar(@arr1);

字符转义

转义单引号

字符串内连续两个单引号表示转义为一个单引号:

1
str1 := 'I can''t do it.';      // I cann't do it.

特殊字符

字符串内的特殊字符,在字符串中再用 '' 引起来,然后用 # 号来书写出 ASCII 码:

1
str1 := 'Hello '#13#10' World';      // Hello \r\n World

多行字符串字面量

Delphi 可以使用多行字符串的字面量,允许换行和里面包含各种字符。

使用三个单引号括起来,里面的所有字符都表示字符串内的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const
strML1 = '''
The quick brown fox jumps
over the lazy dog.
''';
strHTML = '''
<UL>
<LI>Item 1</LI>
<LI>Item 2</LI>
<LI>Item 3</LI>
<LI>Item 4</LI>
</UL>
''';
strJSON = '''
[
{"id" : "1", "name" : "Large"},
{"id" : "2", "name" : "Medium"},
{"id" : "2", "name" : "Small"}
]
''';
strSQL= '''
SELECT *
FROM Customers
WHERE Department = 'R&D'
ORDER BY Name;
''';