zoukankan      html  css  js  c++  java
  • 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自带例子:
    DemosDelphiWin32VCLWin32MemMgrSimpleShareMem
    在主程序和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: empUnicode-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

  • 相关阅读:
    介绍下自己的Delphi学习环境
    我所理解的Delphi中的数组类型
    字符串的基本操作
    以太网网络变压器的作用
    S3C2416 2D加速
    DM9000AEP调试的时候注意事项
    设置activity背景图片
    如何從現有的share library開發!?
    struct mntent linux挂载信息读取
    Qt中Qstring,char,int,QByteArray之间到转换
  • 原文地址:https://www.cnblogs.com/findumars/p/3566148.html
Copyright © 2011-2022 走看看