再探Delphi字符串

閒來無事,又開始扒拉起Delphi的源碼,此次發現一個比較有意思的函數StringCodePage,做用是返回傳入字符串的CodePage。至於什麼是CodePage,暫且認爲是字符編碼吧。windows

先測試一把:ide

var
  s1: AnsiString;
  s2: WideString;
  s3: UTF8String;
  cp1, cp2, cp3: Word;
begin
  s1 := '123abc中國';
  s2 := '123abc中國';
  s3 := '123abc中國';
  cp1 := StringCodePage(s1); //936   - GBK(簡體中文)
  cp2 := StringCodePage(s2); //1200  - UCS-2LE Unicode 小端序
  cp3 := StringCodePage(s3); //65001 - UTF-8 Unicode
end;

來看下是怎麼實現的:函數

function StringCodePage(const S: UnicodeString): Word; overload;
begin
  if S <> '' then
    Result := PWord(PByte(S) - 12)^          // StrRec.codePage
  else
    Result := Word(DefaultUnicodeCodePage);
end;

原來字符串首地址逆向偏移12個字節所存放的Word型數據就是該字符串的CodePage信息。註釋裏出現了一個StrRec,看一下是何方神聖:測試

StrRec = packed record
  codePage: Word;  //代碼頁
  elemSize: Word;  //元素大小
  refCnt: Integer; //引用計數
  length: Integer; //字符串長度
end;

兩個Word兩個Integer恰好12B,那就看下全部字節吧:編碼

function BytesToHex(p: PByte; len: Integer): string;
var
  i: Integer;
begin
  Result := '';
  for i := 0 to len - 1 do
    Result := Result + ' ' + IntToHex((p + i)^, 2);
  Result := Trim(Result);
end;

var
  s1: AnsiString;
  s2: WideString;
  s3: UTF8String;
  s4: UnicodeString;
  hex1, hex2, hex3, hex4: string;
begin
  s1 := '123abc中國';
  s2 := '123abc中國';
  s3 := '123abc中國';
  s4 := '123abc中國';
  hex1 := BytesToHex(PByte(s1) - 12, Length(s1) + 12);
  hex2 := BytesToHex(PByte(s2) - 12, Length(s2) * 2 + 12);
  hex3 := BytesToHex(PByte(s3) - 12, Length(s3) + 12);
  hex4 := BytesToHex(PByte(s4) - 12, Length(s4) * 2 + 12);
//hex1: A8 03 01 00 01 00 00 00 0A 00 00 00 31 32 33 61 62 63 D6 D0 B9 FA
//hex2: 86 06 D4 26 5E AC 00 18 10 00 00 00 31 00 32 00 33 00 61 00 62 00 63 00 2D 4E FD 56
//hex3: E9 FD 01 00 01 00 00 00 0C 00 00 00 31 32 33 61 62 63 E4 B8 AD E5 9B BD
//hex4: B0 04 02 00 01 00 00 00 08 00 00 00 31 00 32 00 33 00 61 00 62 00 63 00 2D 4E FD 56
end;

查一下各個字符的編碼備用:123abc的ASCII碼就沒必要說了;的GBK編碼D6D0,Unicode編碼4E2D,UTF-8編碼E4B8AD的GBK編碼B9FA,Unicode編碼56FD,UTF-8編碼E59BBDcode


先看s1,codePage:A8 03,elemSize:01 00,refCnt:01 00 00 00,length:0A 00 00 00,字符串內容:31 32 33 61 62 63 D6 D0 B9 FAblog

字符串內容比較好理解,與具體編碼也都一一對上了,可是其它的又是怎麼回事呢?用PWordPInteger去取的話又是沒問題的啊!實際上是編譯器大端小端的問題,至於大小端問題這裏不討論,知道這裏用的是小端就能夠了。字符串

小端來解析s1,codePage:03 A8=936,elemSize:00 01=1,refCnt:00 00 00 01=1,length:00 00 00 0A=10。編譯器


s2就比較頭大了,前8個字節老是變來變去,length:00 00 00 10=16,字符串內容按小端來解析也是沒問題的。源碼

其中有這樣一段代碼:

function _WStrLen(const S: _WideStr): Integer; inline;
{$IFDEF CPU64BITS}
begin
  Result := 0;
  if Pointer(S) <> nil then
    {$IFDEF MSWINDOWS}
    Result := PInteger(PByte(S) - 4)^ shr 1;
    {$ELSE}
    Result := PInteger(PByte(S) - 4)^;
    {$ENDIF}
end;
{$ELSE !CPU64BITS}
begin
  Result := IntPtr(S);
  if Result <> 0 then
    {$IFDEF MSWINDOWS}
    Result := PInteger(PByte(Result - 4))^ shr 1;
    {$ELSE}
    Result := PInteger(PByte(Result - 4))^;
    {$ENDIF}
end;
{$ENDIF !CPU64BITS}

意思是說求WideString的長度時,windows平臺下還須要右移一位。


s3,codePage:FD E9=65001,elemSize:00 01=1,refCnt:00 00 00 01=1,length:00 00 00 0C=12,字符串內容與UTF-8編碼一致。


s4小端來解析,codePage:04 B0=1200,elemSize:00 02=2,refCnt:00 00 00 01=1,length:00 00 00 08=8,字符串內容UCS-2LE編碼一致。

這裏發現一個重大問題:WideStringUnicodeString雖然在內容上同樣,但具體實現倒是不一樣的。

var
  s1: AnsiString;
  s2: WideString;
  s3: UTF8String;
  s4: UnicodeString;
begin
  Writeln('@s1        : ', Cardinal(@s1));
  Writeln('@s1[1]     : ', Cardinal(@s1[1]));
  Writeln('@PByte(s1) : ', Cardinal(@PByte(s1)));
  Writeln('PByte(s1)  : ', Cardinal(PByte(s1)));
  s1 := '123abc中國';
  Writeln('s1 : ', s1);
  Writeln('@s1        : ', Cardinal(@s1));
  Writeln('@s1[1]     : ', Cardinal(@s1[1]));
  Writeln('@PByte(s1) : ', Cardinal(@PByte(s1)));
  Writeln('PByte(s1)  : ', Cardinal(PByte(s1)));
  Writeln('------------------------');
  Writeln('@s2        : ', Cardinal(@s2));
  Writeln('@s2[1]     : ', Cardinal(@s2[1]));
  Writeln('@PByte(s2) : ', Cardinal(@PByte(s2)));
  Writeln('PByte(s2)  : ', Cardinal(PByte(s2)));
  s2 := '123abc中國';
  Writeln('s2 : ', s2);
  Writeln('@s2        : ', Cardinal(@s2));
  Writeln('@s2[1]     : ', Cardinal(@s2[1]));
  Writeln('@PByte(s2) : ', Cardinal(@PByte(s2)));
  Writeln('PByte(s2)  : ', Cardinal(PByte(s2)));
  Writeln('------------------------');
  Writeln('@s3        : ', Cardinal(@s3));
  Writeln('@s3[1]     : ', Cardinal(@s3[1]));
  Writeln('@PByte(s3) : ', Cardinal(@PByte(s3)));
  Writeln('PByte(s3)  : ', Cardinal(PByte(s3)));
  s3 := '123abc中國';
  Writeln('s3 : ', s3);
  Writeln('@s3        : ', Cardinal(@s3));
  Writeln('@s3[1]     : ', Cardinal(@s3[1]));
  Writeln('@PByte(s3) : ', Cardinal(@PByte(s3)));
  Writeln('PByte(s3)  : ', Cardinal(PByte(s3)));
  Writeln('------------------------');
  Writeln('@s4        : ', Cardinal(@s4));
  Writeln('@s4[1]     : ', Cardinal(@s4[1]));
  Writeln('@PByte(s4) : ', Cardinal(@PByte(s4)));
  Writeln('PByte(s4)  : ', Cardinal(PByte(s4)));
  s4 := '123abc中國';
  Writeln('s4 : ', s4);
  Writeln('@s4        : ', Cardinal(@s4));
  Writeln('@s4[1]     : ', Cardinal(@s4[1]));
  Writeln('@PByte(s4) : ', Cardinal(@PByte(s4)));
  Writeln('PByte(s4)  : ', Cardinal(PByte(s4)));
  Readln;
end.

結果如圖,很少解釋了。

相關文章
相關標籤/搜索