閒來無事,又開始扒拉起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編碼E59BBD
。code
先看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 FA
。blog
字符串內容比較好理解,與具體編碼也都一一對上了,可是其它的又是怎麼回事呢?用PWord
和PInteger
去取的話又是沒問題的啊!實際上是編譯器大端
、小端
的問題,至於大小端問題這裏不討論,知道這裏用的是小端
就能夠了。字符串
按小端
來解析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編碼一致。
這裏發現一個重大問題:WideString
和UnicodeString
雖然在內容上同樣,但具體實現倒是不一樣的。
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.
結果如圖,很少解釋了。