delphi.指针.PChar

时间:2021-10-01 23:44:49

2:由于D分开Ansi/Unicode的两种完全不同的编绎器,即: Ansi.Char=AnsiChar; Unicode.Char=WideChar

所以在此文中,PChar针对于PAnsiChar, 对于PWideChar,需要做其它处理,请注意

PChar是一个指针,它指向了一个字符串内容的指针,与Pointer相比,它有数据类型(Char)。

所以,有人也喜欢拿它作为内存块的存储,进行一种Buffer的封装,因为它与Pointer相比,移动,转换方便,居家必备啊(请看用法二)

 用法一:

1 var 2 p: PChar; 3 s: string; 4 begin 5 s := ‘abc‘; 6 p := PChar(s);

最常用的代码,进行string与PChar的数据类型转换,在各类API中,经常用到。

其它相关用法是:赋值时,进行移动:+- 

p := PChar(s) + 1;                     // p 指向s[2]

p := PChar(s) - sizeof(Integer);  // p 指向string.len

注意:

string在其内容必定是Char = #0。即s := ‘abc‘; 在s 申请的(3 + 1) * sizeof(char),其中的1就是为#0准备的。

// ShowMessage(IntToStr(Ord((PChar(s) + Length(s))^)));

用法二: 用PChar进行缓存处理

1 type 2 PMyString = ^TMyString; 3 TMyString = record 4 buf: PAnsiChar; 5 len: Integer; 6 buf_len: Integer; 7 end; 8 9 // 初始化+分配空间 10 procedure string_init(var s: TMyString; buf_len: Integer); 11 begin 12 FillChar(s, SizeOf(s), 0); 13 s.buf := AllocMem(buf_len); 14 s.buf_len := buf_len; 15 end; 16 17 // 反初始化+释放空间 18 procedure string_uninit(var s: TMyString); 19 begin 20 if s.buf_len > 0 then 21 FreeMem(s.buf); 22 FillChar(s, sizeof(s), 0); 23 end; 24 25 // 写数据(任意数据) 26 procedure string_write(var s: TMyString; buf: Pointer; len: Integer); overload; 27 begin 28 if len + s.len > s.buf_len then 29 begin 30 Inc(s.buf_len, len * 2); 31 ReallocMem(s.buf, s.buf_len); 32 end; 33 if len > 0 then 34 begin 35 Move(buf^, (s.buf + s.len)^, len); 36 Inc(s.len, len); 37 end; 38 end; 39 40 // 写数据(字符串数据) 41 procedure string_write(var s: TMyString; const AData: string); overload; 42 begin 43 string_write(s, PAnsiChar(AData), Length(AData) * sizeof(Char)); 44 end; 45 46 // 读数据(只针对字符串),读完后清除数据 47 function string_read(var s: TMyString): string; 48 begin 49 if s.len > 0 then 50 begin 51 SetString(Result, s.buf, s.len); 52 s.len := 0; 53 end else 54 Result := ‘‘; 55 end;

以上是个简单的用PChar进行buffer缓存的函数,在写(string_write)的过程中,其实就是一个简单PChar的+-处理,只是一个延伸方法。

读操作,只写了个字符串,其它数据,如integer, double之类的,其实就是一个指针转换的问题,如:       

1 result := PByte(s.buf)^; 2 result := PInteger(s.buf + 1)^; 3 result := PCardinal(s.buf + 1 + 4)^; 4 result := PDouble(s.buf + 1 + 4 + 4)^; 5 result := PMsg(s.buf + 1 + 4 + 4 + 8)^.wParam;

还有个操作是string_delete的,留着有兴趣的人自行处理:)

用法三:

不知是否看过代码:Classes.pas::TParser.NextToken,里面的代码,进行解析字符串写的非常有意思。

大概规则是:当遇到某需的字符,然后找到结束符,然后得到一个串,根据规则,让那个串转为integer, string...

然后,我就学会了用PChar去解析各类字串,我得说那代码得赞一个,思路非常有意思,建议一看。

下面例子,大概是NextToken的简化版,将一些逻辑写出来,嗯,,用splitter作个最简单的示例。

1 type 2 TStr = record 3 ptr: PChar; 4 len: Integer; 5 end; 6 7 // 将src的数据,进行分隔,分隔出来的数据放到s中 8 // 成功,表示分隔成功,失败表示结束 9 function splitter(var src, s: TStr): Boolean; 10 var 11 start: PChar; 12 begin 13 result := false; 14 if src.len <= 0 then exit; 15 16 // 1:保存原地址 17 start := src.ptr; 18 // 2: 移动到分隔字符处 19 while (src.len > 0) and not (src.ptr^ in [‘,‘, ‘;‘]) do 20 begin 21 inc(src.ptr); 22 dec(src.len); 23 end; 24 // 3: 检查分隔是否成功 25 result := src.ptr - start > 0; 26 if result then 27 begin 28 // 4: 成功,进行s赋值 29 s.ptr := start; 30 s.len := src.ptr - start; 31 // 5: 跳过分隔字符,等待下一次分隔 32 while (src.len > 0) and (src.ptr^ in [‘,‘, ‘;‘]) do 33 begin 34 inc(src.ptr); 35 dec(src.len); 36 end; 37 end; 38 end; 39 40 function splitter_string(var src: TStr; var s: string): Boolean; 41 var 42 sub: TStr; 43 begin 44 result := splitter(src, sub); 45 if result then 46 SetString(s, sub.ptr, sub.len); 47 end; 48 49 procedure TForm1.Button1Click(Sender: TObject); 50 var 51 src: TStr; 52 data, s: string; 53 begin 54 data := ‘a,b,c‘; 55 src.ptr := PChar(data); 56 src.len := Length(data); 57 58 while splitter_string(src, s) do 59 Memo1.Lines.Add(‘splitter: ‘ + s); 60 end;