zoukankan      html  css  js  c++  java
  • delphi.指针.PChar

    此文是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

        

  • 相关阅读:
    [转]Intellij IDEA快捷键与使用小技巧
    Swoole来实现实时异步任务队列
    php 异步执行脚本
    Centos 7 systemctl和防火墙firewalld命令
    tgz的解压
    error: C++ preprocessor "/lib/cpp" fails sanity check错误解决方法
    Linux 命令详解(三)./configure、make、make install 命令
    LNMP, CentOS7.0+Nginx+Mysql5.7+PHP7环境安装
    phpmailer使用qq邮箱、163邮箱成功发送邮件实例代码
    Mibew Messenger (also known as Open Web Messenger)
  • 原文地址:https://www.cnblogs.com/qiusl/p/4034322.html
Copyright © 2011-2022 走看看