Delphi 2009 では、時として文字列型の内部構造を意識しなければならない事があります。ここでは、Delphi が扱う文字列型の内部構造を解説します。
ShortString (Pascal 文字列)
ShortString...伝統的な Pascal 文字列の構造は以下のようになっています。
要素数
(byte) |
文字列
(文字構成要素: Char*1/AnsiChar) |
| S[0] |
S[1] |
S[2] |
… |
S[n] |
| |
|
|
|
|
"要素数"は文字列を構成する文字構成要素(エレメント)の数...つまり文字列長です。 ShortString では、'ABC' のような文字列は、
要素数
(byte) |
文字列
(文字構成要素: Char*1/AnsiChar) |
| 3 |
'A' |
'B' |
'C' |
| |
|
|
|
このように格納されています。要素数は byte の範囲内でしか格納できないので、ShortString(Pascal文字列) の最大長は 255文字 となります。 また、ShortString(Pascal文字列) に於いて "Byte(S[0])" は "Length(S)" と同義になります。
String*1 / AnsiString (Delphi 2007 までの長い文字列)
Delphi 2007またはそれ以前の String の構造は以下のようになっています。
参照カウンタ
(LongInt) |
要素数
(LongInt) |
文字列
(文字構成要素: Char*1/AnsiChar) |
Null
(8bit) |
| S[1] |
S[2] |
… |
S[n] |
| |
|
|
|
|
|
|
"参照カウンタ" はその名の通り、この文字列の実体が変数によって参照されている数です。参照カウンタが 0 になると文字列が確保していた領域が破棄されます。また、参照カウンタは -1 を指す事があります。これは定数や、定数を代入された変数です。
var S1, S2: AnsiString;
function ReferenceCount(const S: AnsiString): LongInt;
begin
result := LongInt(S);
if result <> 0 then
result := PLongInt(result - SizeOf(LongInt) - SizeOf(LongInt))^;
end;
function ElementCount(const S: AnsiString): LongInt;
begin
result := LongInt(S);
if result <> 0 then
result := PLongInt(result - SizeOf(LongInt))^;
end;
begin
S1 := 'A';
ShowMessage(Format('Reference=%d : Elements=%d', [ReferenceCount(S1), ElementCount(S1)]));
S2 := S1 + 'B';
ShowMessage(Format('Reference=%d : Elements=%d', [ReferenceCount(S2), ElementCount(S2)]));
end;
このようなコードで検証可能です。なお、 Delphi 2007(またはそれ以前) の UTF8String も全く同一の構造となっています("UTF8String = type AnsiString;")。
PChar*1 / PAnsiChar (ヌルで終わる文字列)
C/C++ でお馴染み PChar*1 です。正確には文字列型ではありません。文字配列へのポインタです。
文字列
(文字構成要素: Char*1/AnsiChar) |
| S[0] |
S[1] |
… |
S[n-1] |
Null
(8bit) |
| |
|
|
|
|
文字列の "要素数" を持たないため、8bit-Null(0x00) で文字列を終端する必要があります。Delphi 2007(またはそれ以前) の PChar型は AnsiChar(バイト) 配列へのポインタですが、Delphi 2009 の PChar型は WideChar(ワード) 配列へのポインタとなっています。
WideString
WideString です。
要素数
(LongInt) |
文字列
(文字構成要素: WideChar/Char*2) |
Null
(16bit) |
| S[1] |
S[2] |
… |
S[n] |
| |
|
|
|
|
|
要素数はバイト換算です。'ABC' の要素数は 6 となります。WideString型 は、参照カウンタを持たない16ビット文字の文字列を表し、COM で使われる BSTR と互換性があります。
PWideChar / PChar*2 (ヌルで終わる文字列)
PWideChar です。正確には文字列型ではありません。文字配列へのポインタです。Delphi 2009 での PChar型 はPWideChar型です。
文字列
(文字構成要素: WideChar/Char*2) |
| S[0] |
S[1] |
… |
S[n-1] |
Null
(16bit) |
| |
|
|
|
|
PAnsiChar 同様ですが、終端は 16bit-Null(0x0000) です。
UCS4String
UCS4Stringです。正確には文字列型ではありません。LongWord の動的配列です。
参照カウンタ
(LongInt) |
要素数
(LongInt) |
文字列
(文字構成要素: LongWord) |
| S[0] |
S[1] |
… |
S[n-1] |
Null
(32bit) |
| |
|
|
|
|
|
|
Delphiの他の文字列型とは性格が違います。添え字は 0 から始まり、終端として 32bit-Null(0x00000000) が必要です。PAnsiChar や PWideChar に近い構造です。しかしながら、動的配列なので PAnsiChar や PWideChar と違いメモリ確保の必要はありません。
UnicodeString / String*2 / AnsiString (Delphi 2009の長い文字列)
Delphi 2009 の String(UnicodeString) / AnsiString の構造は以下のようになっています。Systemユニット(System.pas)で定義されているレコード型「StrRec」でも、この構造を確認できます。
コードページ
(WORD) |
要素サイズ
(WORD) |
参照カウンタ
(LongInt) |
要素数
(LongInt) |
文字列
(文字構成要素: 任意) |
Null
(サイズは要素サイズに依存) |
| S[1] |
S[2] |
… |
S[n] |
| |
|
|
|
|
|
|
|
|
Delphi 2007 またはそれ以前の AnsiString の構造に "コードページ" と "要素サイズ" を付加したような構造になっています。コードページは StringCodePage()、要素サイズは StringElementSize()、参照カウンタは StringRefCount() でそれぞれ取得可能です。
例えば日本語OS上で、AnsiString に 'ABC' を代入した時の状態は以下のようになります。
コードページ
(WORD) |
要素サイズ
(WORD) |
参照カウンタ
(LongInt) |
要素数
(LongInt) |
文字列
(文字構成要素: AnsiChar) |
Null
(8bit) |
| 932 |
1 |
-1 |
3 |
'A' |
'B' |
'C' |
| |
|
|
|
|
|
|
|
UnicodeString に 'ABC' を代入した時の状態は以下のようになります。要素数は WideString と違い、文字構成要素(エレメント)数となっています。
コードページ
(WORD) |
要素サイズ
(WORD) |
参照カウンタ
(LongInt) |
要素数
(LongInt) |
文字列
(文字構成要素: Char*2/WideChar) |
Null
(16bit) |
| 1200 |
2 |
-1 |
3 |
'A' |
'B' |
'C' |
| |
|
|
|
|
|
|
|
以下のようなコードで検証できます。
var A: AnsiString;
U: UnicodeString;
function ElementCount(const S: RawbyteString): LongInt; overload;
begin
result := LongInt(S);
if result <> 0 then
result := PLongInt(result - SizeOf(LongInt))^;
end;
function ElementCount(const S: UnicodeString): LongInt; overload;
begin
result := LongInt(S);
if result <> 0 then
result := PLongInt(result - SizeOf(LongInt))^;
end;
begin
A := 'ABC';
ShowMessage(Format('CodePage=%d : ElementSize=%d : Reference=%d : ElementCount=%d',
[StringCodePage(A), StringElementSize(A), StringRefCount(A), ElementCount(A)]));
U := 'ABC';
ShowMessage(Format('CodePage=%d : ElementSize=%d : Reference=%d : ElementCount=%d',
[StringCodePage(U), StringElementSize(U), StringRefCount(U), ElementCount(U)]));
end;
補足
"文字列" の領域が文字列として一般的にアクセスされる領域で、変数へのポインタは通常この "文字列" の先頭を指します。ソースコードで S[1] を渡しているものをよく見かけるのはそういう事です。また、"文字列" の中に Null が含まれていると、それはヌルターミネートとなるので、文字列中に Null を含める事はできません。
Delphi の文字/文字列の対応は以下の表を参照して下さい。
| |
Char |
PChar |
String |
Delphi 2007
(またはそれ以前) |
AnsiChar |
PAnsiChar |
String
AnsiString |
| Delphi 2009 |
WideChar |
PWideChar |
UnicodeString |
*1 Delphi 2007(またはそれ以前)
*2 Delphi 2009
Connect with Us