http://www.cnblogs.com/qiusl/p/4034322.html
此文是delphi.指針.應用姊妹篇,想細化一下PChar應用,所以有了此文。
注意:
1:此文講的是PChar與字符串相關操作,其它方法暫不多講。
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;
原諒俺,一直不知道這種解析的方法叫啥名字(有知道的請告訴一下),或者就是個字符解析的邏輯而已?不過感覺不像。
也許會有人問,為啥這么折騰,簡單的分隔,用TString再加個屬性設置,就可以得到結果了。
以上,只是一個示例,只是大概邏輯。也並非想進行分隔,而且在某些場合,是盡量少用字符串使用的,然后,這種法子就用處了。
這種方法應用場合,在字符串處理中進行:語法解析,真是無往不利,比如:表達式,XML,JSON,HTTP。。。
這種處理方法:PChar從頭到尾掃描一次,然后就結束,中間穿插取數據的處理。所以速度非快。
且中間,如果需要可以不產生任何與內存分配/釋放的處理(string操作需要GetMem+FreeMem),只記錄地址+長度。
上例只是一簡單的處理,還有是利用case,進行匹配各個字符,如XML中的字符"<", "/", ">",然后進行數據處理。
這部分內容,估計對解析有興趣的才會看了,所以不再細寫了。:D
注意點:
1:p: PChar; p^ = 'a',如果不小心"^"未寫,變成: p = 'a',編繹不出錯,但結果不正確,少一個^符號非常難查找。
總結:
有些東西很細化,總得說來PChar操作多種多樣,因為指針本身就是自由度比較高的東西,再加上一些方法,組合起來就不用說了。
所以,也沒法說的太清,感覺說來說去像是在繞圈子,還是說那些東西,所以,先寫到這里吧。:)
水平有限,如有雷同,就是盜版!
2014.10.21 by qsl

