Delphi String的散漫记录,真是知识无数,陷阱无数
真是膜拜Delphi C++ Builder编译器的作者们,要下多少苦功夫才能解决如此之多的问题,制造出一个神级作品给世人享用。另外以我的编程经验所能想到很麻烦但却是必须的还有两个地方,一个是Format函数,另一个是类型转换。有空看看FreePascal的源码可以略窥一二。其实我也是一个疯狂的政经爱好者,本来也不是很吊美国,觉得我们迟早各方面,包括最先进的科学技术,都能赶上的他们的。但是想想这些神级的工程师,包括Borland早期的和现在的Embarcadero,心里多少有些发怵。美国,虽然也有Facebook创始人这样不劳而获的人(至少真的不值这么多钱),但是更多的还是为了金钱而疯狂工作的工程师国家,这一点任正非的讲话里也提到过。虽然现在也还是走下坡路了,但是底子仍在。
PwideChar()强制转化的话会重新分配内存,这个内存是局部的,函数已结束内存就释放了。GetMem 和 StringToWideChar结合使用,函数结束也不会释放内存,但是这样需要在程序运行完毕手动释放内存了。
str1: String[6]; {指定大小不能超过 255} {多给了会被截断}
如果你的字符串长度不超过 255,完全可以用 ShortString,用法同 String,并且可以用在 Dll 中:
var
a: ShortString;
begin
SetLength(a, 64);
Delete(a, 1, 5);
end;
DLL里面是可以用String,只要传递的参数不是String就不用担心。参数可以用PChar,函数内部再转换
shorstring不是以null结尾的。
统计字符串长度时不包括 Null 结束字节.
用FastMM吧,不用DLL也可以实现DLL和EXE传递字符串了,下载网址
http://fastmm.sourceforge.net/
Dephi 2006里的内存管理器已经是 FastMM 了
开始我也觉得迷惑,后来打开 GetMem.inc 一看就什么都明白了:)(里面的代码就是FastMM,作者就是:Pierre le Riche)
至于怎么用可以看Delphi自带例子:
Demos\DelphiWin32\VCLWin32\MemMgr\SimpleShareMem
在主程序和DLL的项目引用第一行加上: SimpleShareMem, 就可以了
{在没有给 str 赋值以前, 既然声明了, 就有了指针地址(@str):}
ShowMessage(IntToStr(Integer(@str))); {1244652; 这是在栈中的 str 的指针地址}
{但现在还没有分配真正储存字符串内存}
ShowMessage(IntToStr(Integer(str))); {0; 0 就是 null}
{通过实际地址获取字符串, 其中的 pc 是前面定义的字符指针}
pc := PChar(Integer(str));
ShowMessage(pc); {Delphi}
A := 'Delphi';
//此时A的引用计数是-1,原因是'字符串'存储在静态数据区,
编译的时候地址就定了,属于常量~也就是说,它是不能动态地释放的;
{向左偏移 4 个字节就是字符串长度的位置, 读出它来(肯定是5):}
pint := PInteger(Integer(str) - 4);
ShowMessage(IntToStr(pint^)); {5}
{向左偏移 8 个字节就是字符串的引用计数, 读出它来(肯定是3):}
pint := PInteger(Integer(str) - 8);
ShowMessage(IntToStr(pint^)); {3}
//字符串 < > 字符数组
var
arr: array[0..5] of Char;
str: string;
begin
{可以把字符数组直接赋给字符串变量}
str := arr;
{但不能把一个字符串变量赋给字符数组}
//arr := str; {错误; 这需要用其他手段实现, 譬如复制或移动内存}
{其实字符串内部也是包含了一个字符数组, 所以能索引访问, 不过它的索引起始于 1}
ShowMessage(str[1]); {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;
如果自己调用api,最快的方法是用pchar转换,保证最后一个字节是null。
获取所有汉字与 Unicode 的对照表
var
w: WideString;
i: Integer;
s: string;
List: TStringList;
begin
List := TStringList.Create;
for i := $4e00 to $9fa5 do
begin
s := #36 + IntToHex(i,4); {#36 是 $ 字符}
w := WideChar(i);
List.Add(s + '=' + w);
end;
List.SaveToFile('c:\temp\Unicode-Hz.txt');
List.Free;
end;
百度上还发现一奇技淫巧:Alt + X 组合键,MS Word 也会将光标前面的字符同其十六进制的四位 Unicode 编码进行互相转换。
似乎可以拿这个做密码啊,神仙都没法知道。
n1 := lstrlen(p);
n2 := lstrlen(buf);
n1 := lstrlenA('Delphi 的魅力');
n2 := lstrlenW('Delphi 的魅力');
ExtractStrings 函数就是, 譬如:
var
str: string;
num: Integer;
List: TStrings;
begin
str := 'e,1|w,2|s,3|n,4|v,5|';
List := TStringList.Create;
num := ExtractStrings(['|'], [], PChar(str), List);
ShowMessage(IntToStr(num)); {num 是分隔符的个数}
ShowMessage(List.Text); {List 是分割后的列表}
List.Free;
end;
lstrcpyn - 复制字符串, 同时指定要复制的长度
lstrcpy - 复制字符串
lstrcat - 合并字符串
IsCharAlphaNumeric - 是否是个文字(字母或数字)
IsCharAlpha - 是否是个字母
c := #19975; {万}
c := #$4E07; {万}
把字符串复制到剪贴板
uses Clipbrd;
Clipboard.SetTextBuf(PChar(str));
Delphi字符串、PChar与字符数组之间的转换
设有以下三个变量:
var
s:string;
p:pchar;
a:array[1..20] of char;
那么三者之间的转换如下:
1、字符串到PChar
p:=PChar(s);
2、PChar到字符串
s:=p;
3、PChar到字符数组
StrCopy(@a,p);
4、字符数组到PChar
PChar(@a);
5、字符串与字符数组之间的转换就只有通过PChar来中转了。例如下面这个例子:
procedure TForm1.btn1Click(Sender: TObject);
var
str:array[1..10] of char;
begin
StrCopy(@str,PChar(mmo1.Text));
mmo2.Text:=PChar(@str);
end
两行代码的前后位置对调一下 ,运行结果就不同了
是 Delphi 对字符串优化所造成的结果(Delphi 的 copy-on-write 技术)
AnsiString 可以直接当内存来使用,它不只可以存放字符,而是可以存放任何东西,你甚至可以将一个图片的数据存入 AnsiString 的内存块中。
Length 函数对于 ShortString 和 AnsiString 来说返回的是它们所存放的字符串的字节数,而不是字符数。
Length 函数对于 WideString 来说,返回的就是字符数,而不是字节数
WideString 没有引用计数。其实 WideString 是为了方便使用 COM 而产生的,也就是 BSTR 字符串。
UniodeString,增加了 codePage 和 elemSize 域。
PStrRec = ^StrRec;
StrRec = packed record
codePage: Word; // 代码页:Unicode、UTF-8、UTF-16、GB2312
elemSize: Word; // 元素大小:一字符占几个字节
refCnt: Longint; // 引用计数:字符串被几个字符串变量使用
length: Longint; // 字符串长度:字节数
end;
在 system 单元中还定义了 UTF8String 和 UCS4String 类型的字符串,定义如下:
UTF8String = type AnsiString(65001);
UCS4String = array of UCS4Char; { UCS4Char = type LongWord; }
RawByteString = type AnsiString($ffff);
这种类型的变量在接收任何格式的字符串时,都会保持源字符串的内存格式,不做任何改动。
var
Str: String;
P: PCardinal;
X: PWord;
begin
Memo1.Clear;
Str:=Self.ClassName; { TForm3 }
Memo1.Lines.Add(Str);
P := PCardinal(Str);
Dec(P); { 向前移动 4 个字节 }
Memo1.Lines.Add(IntToStr(P^)); { 结果 6 字符串长度 }
Dec(P); { 再向前移动 4 个字节 }
Memo1.Lines.Add(IntToStr(P^)); { 结果 1 引用计数 }
X:=PWord(Integer(P)-2); { 再向前移动 2 个字节 }
Memo1.Lines.Add(IntToStr(X^)); { 结果 2 字符宽度 }
X:=PWord(Integer(X)-2); { 再向前移动 2 个字节 }
Memo1.Lines.Add(IntToStr(X^)); { 结果 1200 UTF-16 代码页注册为代码页 1200 }
X^:=60001; {字符编码居然可以改变}
end;
1200—UCS-2LE Unicode 小端序
1201—UCS-2BE Unicode 大端序
65000—UTF-7 Unicode
65001—UTF-8 Unicode
不同的厂商对同一个字符集编码使用各自不同的名称:UTF-8在IBM称作代码页1208,在微软称作代码页65001,在SAP称作代码页4110.
微软在Windows操作系统没有转向UTF-16作为内码实现之前(也就是在Windows 2000之前),针对不同的使用地区与国家,定义了一系列的支持不同语言字符集的代码页,被称作"Windows(或ANSI)代码页"。代表性的是实现了ISO-8859-1的代码页1252.
Windows-1252与ISO-8859-1并不完全一致。ISO-8859-1在0x80-0x9F范围的控制字符,在Windows-1252中被可打印字符取代。由于在web网页中,ASCII控制字符不起作用,所以网页一般用Windows-1252代码页标记替代ISO-8859-1标记。
chcp命令带一个整数参数,则改变命令行窗口的当前代码页为参数所指定。
最牛的一篇对String的解释文章:
http://www.cnblogs.com/PocketZ/archive/2013/03/26/2983583.html