Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2018年11月28日] [更新 2018年11月28日] Delphi 一般・その他
AnsiString 型の文字列と PAnsiChar 型の文字列
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32
[整理番号 - 0740]







ユニコード版の Delphi における AnsiString 型と PAnsiChar 型


Delphi 2009 以降の Delphi には String 型,PChar 型,AnsiString 型と PAnsiChar 型の文字列の型があります.
AnsiString 型は Delphi 2007 以前の String 型に相当します.また,PAnsiChar 型は Delphi 2007 以前の PChar 型に相当します.
本ページは Delphi 2009 以降で AnsiString 型と PAnsiChar 型を使用する際の参考と,それらの型の変換に関するサンプルです.詳細については参考リンクの記事等で確認してください.



[備考 1]
Delphi 2007 以前のバージョンの Delphi とは,Delphi (Delphi 1), Delphi 2, Delphi 3 / 3.1, Delphi 4, Delhi 5, Delphi 6, Delphi 7, Delphi 2005, Dephi 2006, Delphi 2007 のことです.
これらを非ユニコード版の Delphi あるいは Ansi 版の Delphi と言います.

[備考 2]
MECSUtils に,AnsiString 関係とユニコードの文字列の変換関係のルーチンがあります.


[備考 3]
ANSI (アンシ) は,American National Standards Institute (米国国家規格協会) の略称です.
日本語 Windows で ANSI と呼ばれているのは,Microsoft が独自に拡張した Shift_JIS コード体系のことを指していて,コードページは 932 (CP932) です.コードページ 932 の名称が Shift_JIS です.これが ANSI の規格になっています.

つまり,Ansi 版 Delphi とは,ANSI (米国国家規格協会) の規格に基づいた文字符号化方式を採用した Delphi ということになります.韓国や中国等,他の国では同じ Ansi 版の Delphi であってもコードページが違います.
日本においては ANSI と言う用語は Shift_JIS と同じ意味で使用されているようですが,この場合の ANSI は,正確にはコードページのことになります.
Shift_JIS は,UTF-8 の場合と同じように 「-」(ハイフン) を使用してしまうことか多いのですが,アンダースコアを使用するのが正確な表記です.





01_ AnsiString 型の文字列の長さをバイト数で取得


Ansi 版の Delphi では文字列の長さをバイト単位で取得していました.
Length 関数の引数が String 型の文字列の場合,戻り値は文字数 (エレメント数) です.Lenght 関数の引数を AnsiString 型の文字列にした場合の戻り値はバイト数です.Length 関数の引数が変数ではなく,文字列そのものである場合,Length 関数は文字数 (エレメント数) を返します.
AnsiString 型の文字列の長さをバイト数で取得したい場合,System.AnsiStrings.StrLen 関数も使用できます.



図1
AnsiString 型の文字列のバイト長さ

リスト1
implementation

uses
  System.AnsiStrings;

{$R *.dfm}

//=============================================================================
//  AnsiString 型の文字列の長さの取得
//  Length 関数を使用
//  Length 関数は引数が AnsiString 型であればバイト数を返す
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LAnsiText : AnsiString;
  LInteger  : Integer;
begin
  Memo1.Lines.Clear;

  LAnsiText := 'ABC日本語文字列';
  Memo1.Lines.Add(LAnsiText);

  LInteger := Length(LAnsiText);
  Memo1.Lines.Add('文字列長さ : ' + IntToStr(LInteger) + ' バイト');
end;

//=============================================================================
//  AnsiString 型の文字列の長さの取得
//  System.AnsiStrings.StrLen 関数はバイト数を返す
//
//  このコードの実行には uses に System.AnsiStrings が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LInteger : Integer;
begin
  Memo1.Lines.Clear;

  Memo1.Lines.Add('ABC日本語文字列');

  LInteger := System.AnsiStrings.StrLen('ABC日本語文字列');
  Memo1.Lines.Add('文字列長さ : ' + IntToStr(LInteger) + ' バイト');
end;


次のサンプルで使用している文字列の 1 番目の文字は「𠮷野屋」の先頭の文字である「𠮷」で,部首は「つち」です.この文字はサロゲートペアな文字です.2 番目の文字は「吉 (キチ)」 という文字で部首は「さむらい」です.3 番目の文字は中国の簡体字の「你」です.

この文字列を TMemo に表示すると下図のようになります.AnsiString 型では,サロゲートペアの文字と中国の簡体字は文字データとして正常に格納することができません.
String 型の文字列の場合,Length 関数で取得した値はエレメント数と呼ばれる値です.エレメント数は文字列の領域を設定する際にも必要となる値です.MECSUtils の MecsLength 関数を使用すると表示通りの文字数が取得できます.



図2
サロゲートペアな文字が含まれている文字列
  • 順番に
  • String 型の文字列を表示
  • AnsiString 型に代入した結果を表示
  • Length 関数で取得した String 型の文字列のエレメント数
  • MECSUtils.MecsLength 関数で取得した String 型の文字列の文字数
  • ByteLength 関数で取得した String 型の文字列のバイト数

リスト2
implementation

uses
  MECSUtils;

{$R *.dfm}

//=============================================================================
//  String 型の文字列の長さ
//  サロゲートペアな文字がある場合
//
//  このコードの実行には uses に MECSUtils が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LString  : string;
  LAnsiStr : AnsiString;
  LInteger : Integer;
begin
  Memo1.Lines.Clear;

  LString := #$D842#$DFB7 + '吉' + #$4F60#$597D;
  Memo1.Lines.Add(LString);

  LAnsiStr := LString;
  Memo1.Lines.Add(LAnsiStr);

  LInteger := Length(LString);
  Memo1.Lines.Add('文字列長さ : ' + IntToStr(LInteger) + ' エレメント');

  LInteger := MECSUtils.MecsLength(LString);
  Memo1.Lines.Add('文字列長さ : ' + IntToStr(LInteger) + ' 文字');

  LInteger := ByteLength(LString);
  Memo1.Lines.Add('文字列長さ : ' + IntToStr(LInteger) + ' バイト');
end;


uses 部に追加する System.AnsiStrings の System を省略する場合は,以下のように,そのユニット内の関数を使用する時も System を省略できます.


uses
  AnsiStrings;


  LIndex := AnsiStrings.AnsiPos('語', LAnsiText);




02_AnsiString 型の文字列における文字の位置をバイト単位で取得


Ansi 版の Delphi では,文字列の指定文字の位置は何バイト目かというバイト単位の値で取得していました.AnsiString 型の文字列でも,System.AnsiStrings.AnsiPos 関数を使用すればバイト単位で文字の位置を取得することができます.その例です.
Delphi 2009 以降では,文字列の指定文字の位置は Pos 関数で取得できます.この値は何文字目かという文字単位です.



図3
指定文字の位置をバイト単位で取得

リスト3
implementation

uses
  System.AnsiStrings;

{$R *.dfm}

//=============================================================================
//  AnsiString 型の文字列における指定文字の位置取得
//  System.AnsiStrings.AnsiPos 関数を使用するとバイト単位の位置が取得できる
//
//  このコードの実行には uses に System.AnsiStrings が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LString   : string;
  LAnsiText : AnsiString;
  LIndex    : Integer;
begin
  Memo1.Lines.Clear;

  LString   := 'ABC日本語文字列';
  LAnsiText := 'ABC日本語文字列';
  Memo1.Lines.Add(LString);

  // LIndex := Pos('語', LAnsiText); だと文字単位になる
  LIndex := System.AnsiStrings.AnsiPos('語', LAnsiText);
  Memo1.Lines.Add('文字位置 : ' + IntToStr(LIndex) + ' バイト目');

  LIndex := Pos('語', LString);
  Memo1.Lines.Add('文字位置 : ' + IntToStr(LIndex) + ' 文字目');
end;




03_Windows API の関数で PAnsiChar 型の文字列を使用


Windows API では基本的に Null 終端文字列の型である PChar あるいは PAnsiChar 型の文字列を使用することになります.Windwos API で PAnsiChar 型の文字列を使用する際は,Ansi 版の関数を使用しないと正常に動作しないかエラーとなります.Ansi 版の関数の末尾は A となっています.
以下は ShellExecute 関数を使用してメモ帳を起動する例です.

Unicode 版の Delphi では Windows API の関数を使用するのに AnsiString 型や PChar 型の Ansi 文字列を使用する必然性はありません.ただし,Windows API の関数やサードパーティが提供しているライブラリの中には Ansi 文字列を使用しないと正常に動作しないルーチンもあります.



リスト4
implementation

uses
  ShellAPI;

{$R *.dfm}

//=============================================================================
//  Unicode 版の Delphi における AnsiString 型の文字列の使用
//
//  AnsiString 型,PAnsiChar 型を使用する
//  Windows API の関数で末尾が A の関数の関数が用意されていればそれを使用する
//
//  このコードの実行には uses に ShellAPI が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LFileName : AnsiString;
begin
  LFileName := 'notepad.exe';

  // 末尾に A がない ShellExecute 関数ではエラーとなる
  ShellExecuteA(0, '', PAnsiChar(LFileName), nil, nil, SW_SHOW);
end;

//=============================================================================
//  Unicode 版の Delphi における AnsiString 型の文字列の使用
//
//  AnsiString 型,PAnsiChar 型を使用する
//  末尾に A がない ShellExecute 関数で PChar を使用してもメモ帳は起動しない
//
//  このコードの実行には uses に ShellAPI が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LFileName : AnsiString;
begin
  LFileName := 'notepad.exe';

  // メモ帳が起動しない例
  ShellExecute(0, '', PChar(LFileName), nil, nil, SW_SHOW);
end;

//=============================================================================
//  メモ帳が起動する例
//  Unicode 版の Delphi では String 型や PChar 型を使用する
//  わざわざ AnsiString 型を使用する必要はない
//
//  このコードの実行には uses に ShellAPI が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LFileName : string;
begin
  LFileName := 'notepad.exe';
  ShellExecute(0, '', PChar(LFileName), nil, nil, SW_SHOW);
end;


上のコードで AnsiString 型の文字列を PChar 型でキャストしているコードがあります.この結果は例えば下図のような結果となります.この文字列は notepad.exe という文字列ではありません.当然ながらメモ帳を起動することはできないことになります.



図4
AnsiString 型の文字列を PChar 型でキャストした結果

リスト5
procedure TForm1.Button1Click(Sender: TObject);
var
  LAnsiStr : AnsiString;
  LPChar   : PChar;
begin
  LAnsiStr := 'notepad.exe';
  LPChar   := PChar(LAnsiStr);

  ShowMessage(LPChar);
end;




04_String 型の文字列と AnsiString 型の文字列の相互変換


AnsiString 型の文字列を String 型の文字列に代入する例と,String 型の文字列を AnsiString 型の文字列に代入する例です.
いずれの場合もそのまま代入できます.


[備考]
このサンプルでは暗黙の型キャスト (型変換) が行われます.この時,元のコードポイント (文字コード) の値が代入先にコピーされるわけではありません.できるだけ同じ文字になるように変換されてデータが格納されます.元の文字にない文字は失われるか別の文字になります.したがって,変換後の文字列を元の文字列の型の変数に代入しても,元の文字列と同じになる保証はありません.
このため「データの損失の可能性がある」や「暗黙のキャスト」の警告が表示されます.

このことは,これ以降のサンプルでも同じです.



図5
String 型と AnsiString 型の相互変換
  • 暗黙の型変換が行われるのでそのまま代入可能

リスト6
//=============================================================================
//  AnsiString 型の文字列を String 型の文字列に変換
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LAnsiText : AnsiString;
  LStrText  : string;
begin
  Memo1.Lines.Clear;

  LAnsiText := 'ABC日本語文字列';
  LStrText  := LAnsiText;

  Memo1.Lines.Add(LStrText);
end;

//=============================================================================
//  String 型の文字列を AnsiString 型の文字列に変換
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LStrText  : string;
  LAnsiText : AnsiString;
begin
  Memo1.Lines.Clear;

  LStrText  := 'ABC日本語文字列';
  LAnsiText := LStrText;

  // 暗黙の型キャスト (AnsiString 型から Stirng 型の変換が行われる)
  Memo1.Lines.Add(LAnsiText);
end;




05_String 型の文字列を PAnsiChar 型の文字列に変換


String 型の文字列を PAnsiChar 型の文字列に変換する例です.
Windows API 等,DLL 内の関数の Null 終端文字列の引数では,あらかじめその文字列の領域を確保しなければならないことがあります.その意味では,メモリ領域を確保する方法が無難と言えます.確保するメモリサイズは Null 終端文字の分 (1 文字) も必要です.

結果の文字列が PAnsiChar 型の文字列なので, メッセージを表示するダイアログの関数には,末尾に A がある MessageBoxA 関数を使用しています.



[備考]
String 型あるいは AnsiString 型の文字列の領域 (サイズ) は SetLength 手続きで設定できますが,Null 終端文字列の領域は AllocMem 等の関数を使用して確保します.



図6
String 型を PAnsiChar 型に変換

リスト7
implementation

uses
  AnsiStrings;

{$R *.dfm}

//=============================================================================
//  String 型の文字列を PAnsiChar 型の文字列に変換
//  AnsiString 型の文字列に変換してから PAnsiChar でキャスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LString    : string;
  LAnsiStr   : AnsiString;
  LPAnsiChar : PAnsiChar;
begin
  LString := 'ABC日本語123';

  LAnsiStr   := LString;
  LPAnsiChar := PAnsiChar(LAnsiStr);
  MessageBoxA(Handle, LPAnsiChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  String 型の文字列を PAnsiChar 型の文字列に変換
//  AnsiString 型でキャストして更に PAnsiChar でキャスト
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LString    : string;
  LPAnsiChar : PAnsiChar;
begin
  LString := 'ABC日本語123';

  LPAnsiChar := PAnsiChar(AnsiString(LString));
  MessageBoxA(Handle, LPAnsiChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  String 型の文字列を PAnsiChar 型の文字列に変換
//  Null 終端文字列用のメモリ領域を確保して文字列のデータをコピー
//
//  uses に AnsiStrings が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LString    : string;
  LPAnsiChar : PAnsiChar;
  LAnsiStr   : AnsiString;
begin
  LString := 'ABC日本語123';

  LAnsiStr   := LString;
  LPAnsiChar := AllocMem(Length(LAnsiStr) + 1);
  try
    AnsiStrings.StrPCopy(LPAnsiChar, LAnsiStr);
    MessageBoxA(Handle, LPAnsiChar, '結果', MB_ICONINFORMATION);
  finally
    FreeMem(LPAnsiChar);
  end;
end;




06_AnsiString 型の文字列を PChar 型の文字列に変換


AnsiString 型の文字列を PChar 型の文字列に変換する例です.
Windows API 等,DLL 内の関数の Null 終端文字列の引数では,あらかじめその文字列の領域を確保しなければならないことがあります.その意味では,メモリ領域を確保する方法が無難と言えます.確保するメモリサイズは Null 終端文字の分 (1 文字) も必要です.



[備考]
String 型あるいは AnsiString 型の文字列の領域 (サイズ) は SetLength 手続きで設定できますが,Null 終端文字列の領域は AllocMem 等の関数を使用して確保します.



図7
AnsiString 型を PChar 型に変換

リスト8
//=============================================================================
//  AnsiString 型の文字列を PChar 型の文字列に変換
//  String 型の文字列に変換して PChar でキャスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LAnsiStr : AnsiString;
  LPChar   : PChar;
  LString  : string;
begin
  LAnsiStr := 'ABC日本語日本語';

  LString := LAnsiStr;
  LPChar  := PChar(LString);
  MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  AnsiString 型の文字列を PChar 型の文字列に変換
//  String でキャストして更に PChar でキャスト
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LAnsiStr : AnsiString;
  LPChar   : PChar;
begin
  LAnsiStr := 'ABC日本語日本語';

  LPChar := PChar(string(LAnsiStr));
  MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  AnsiString 型の文字列を PChar 型の文字列に変換
//  String 型の文字列に変換したあとに
//  Null 終端文字列用のメモリ領域を確保してそこにコピー
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LAnsiStr : AnsiString;
  LPChar   : PChar;
  LString  : string;
begin
  LAnsiStr := 'ABC日本語日本語';
  LString  := LAnsiStr;

  LPChar := AllocMem((Length(LString) + 1) * SizeOf(Char));
  try
    StrPCopy(LPChar, LString);
    MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
  finally
    FreeMem(LPChar);
  end;
end;


StrAlloc 関数を使用すると,文字数の指定でメモリ領域の確保ができます.StrAlloc 関数で確保したメモリは StrDispose 関数で解放します.



リスト9
procedure TForm1.Button3Click(Sender: TObject);
var
  LAnsiStr : AnsiString;
  LPChar   : PChar;
  LString  : string;
begin
  LAnsiStr := 'ABC日本語日本語';
  LString  := LAnsiStr;

  LPChar := StrAlloc(Length(LString) + 1);
  try
    StrPCopy(LPChar, LString);
    MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
  finally
    StrDispose(LPChar);
  end;
end;




07_PAnsiChar 型の文字列を String 型の文字列に変換


PChar 型の文字列を AnsiString 型の文字列に変換する例です.



図8
PAnsiChar 型を String 型に変換

リスト10
//=============================================================================
//  PAnsiChar 型の文字列を String 型の文字列に変換
//  AnsiString 型の文字列に変換して String 型に変換
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPAnsiChar : PAnsiChar;
  LString    : string;
  LAnsiStr   : AnsiString;
begin
  LPAnsiChar := 'ABC日本語日本語';

  LAnsiStr := LPAnsiChar;
  LString  := LAnsiStr;
  MessageBox(Handle, PChar(LString), '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PAnsiChar 型の文字列を String 型の文字列に変換
//  AnsiString 型でキャストし String 型に代入
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LPAnsiChar : PAnsiChar;
  LString    : string;
begin
  LPAnsiChar := 'ABC日本語日本語';

  LString := AnsiString(LPAnsiChar);
  MessageBox(Handle, PChar(LString), '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PAnsiChar 型の文字列を String 型の文字列に変換
//  直接 String 型に代入
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LPAnsiChar : PAnsiChar;
  LString    : string;
begin
  LPAnsiChar := 'ABC日本語日本語';

  LString := LPAnsiChar;
  MessageBox(Handle, PChar(LString), '結果', MB_ICONINFORMATION);
end;




08_PChar 型の文字列を AnsiString 型の文字列に変換


PChar 型の文字列を AnsiString 型の文字列に変換する例です.

結果の文字列が AnsiString 型の文字列なので, メッセージを表示するダイアログの関数には,末尾に A がある MessageBoxA 関数を使用しています.



図9
PChar 型を AnsiString 型に変換

リスト11
//=============================================================================
//  PChar 型の文字列を AnsiString 型の文字列に変換
//  String 型の文字列に変換から AnsiString 型に変換
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPChar   : PChar;
  LAnsiStr : AnsiString;
  LString  : string;
begin
  LPChar := 'ABC日本語日本語';

  LString  := LPChar;
  LAnsiStr := LString;
  MessageBoxA(Handle, PAnsiChar(LAnsiStr), '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PChar 型の文字列を AnsiString 型の文字列に変換
//  AnsiString または String キャストして代入
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LPChar   : PChar;
  LAnsiStr : AnsiString;
begin
  LPChar := 'ABC日本語日本語';

  // または1 LAnsiStr := AnsiString(LPChar);
  LAnsiStr := String(LPChar);
  MessageBoxA(Handle, PAnsiChar(LAnsiStr), '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PChar 型の文字列を AnsiString 型の文字列に変換
//  直接 AnsiString 型に代入
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LPChar   : PChar;
  LAnsiStr : AnsiString;
begin
  LPChar := 'ABC日本語日本語';

  LAnsiStr := LPChar;
  MessageBoxA(Handle, PAnsiChar(LAnsiStr), '結果', MB_ICONINFORMATION);
end;




09_PChar 型の文字列を PAnsiChar 型の文字列に変換


PChar 型の文字列を PAnsiChar 型の文字列に変換する例です.
Windows API 等,DLL 内の関数の Null 終端文字列の引数では,あらかじめその文字列の領域を確保しなければならないことがあります.その意味では,メモリ領域を確保する方法が無難と言えます.確保するメモリサイズは Null 終端文字の分 (1 文字) も必要です.

結果の文字列が PAnsiChar 型の文字列なので, メッセージを表示するダイアログの関数には,末尾に A がある MessageBoxA 関数を使用しています.



[備考]
String 型あるいは AnsiString 型の文字列の領域 (サイズ) は SetLength 手続きで設定できますが,Null 終端文字列の領域は AllocMem 等の関数を使用して確保します.



図10
PChar 型を PAnsiChar 型に変換

リスト12
uses
  AnsiStrings;

{$R *.dfm}

//=============================================================================
//  PChar 型の文字列を PAnsiChar 型の文字列に変換
//  AnsiString 型の文字列に変換後,PAnsiChar でキャスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPChar     : PChar;
  LPAnsiChar : PAnsiChar;
  LAnsiStr   : AnsiString;
begin
  LPChar := 'ABC日本語文字列';

  // または LAnsiStr := LPChar;
  // または LAnsiStr := Ansistring(LPChar);
  LAnsiStr   := String(LPChar);
  LPAnsiChar := PAnsiChar(LAnsiStr);

  MessageBoxA(Handle, LPAnsiChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PChar 型の文字列を PAnsiChar 型の文字列に変換
//  String でキャストした後,更に PAnsiChar でキャスト
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
//  
  LPChar     : PChar;
  LPAnsiChar : PAnsiChar;
  LAnsiStr   : AnsiString;
begin
  LPChar := 'ABC日本語文字列';

  LAnsiStr   := AnsiString(String(LPChar));
  LPAnsiChar := PAnsiChar(LAnsiStr);

  MessageBoxA(Handle, LPAnsiChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PChar 型の文字列を PAnsiChar 型の文字列に変換
//
//  AnsiString 型の文字列に変換後,
//  PAnsiChar 型の文字列のメモリ領域を確保して StrPCopy で文字列のデータをコピー
//
//  uses に AnsiStrings が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LPChar     : PChar;
  LPAnsiChar : PAnsiChar;
  LAnsiStr   : AnsiString;
begin
  LPChar := 'ABC日本語文字列';

  LAnsiStr := LPChar;

  LPAnsiChar := AllocMem(Length(LAnsiStr) + 1);
  try
    AnsiStrings.StrPCopy(LPAnsiChar, LAnsiStr);
    MessageBoxA(Handle, LPAnsiChar, '結果', MB_ICONINFORMATION);
  finally
    FreeMem(LPAnsiChar);
  end;
end;




10_PAnsiChar 型の文字列を PChar 型の文字列に変換


PAnsiChar 型の文字列を PChar 型の文字列に変換する例です.
Windows API 等,DLL 内の関数の Null 終端文字列の引数では,あらかじめその文字列の領域を確保しなければならないことがあります.その意味では,メモリ領域を確保する方法が無難と言えます.確保するメモリサイズは Null 終端文字の分 (1 文字) も必要です.



[備考]
String 型あるいは AnsiString 型の文字列の領域 (サイズ) は SetLength 手続きで設定できますが,Null 終端文字列の領域は AllocMem 等の関数を使用して確保します.



図11
PAnsiChar 型を PChar 型に変換

リスト13
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPAnsiChar : PAnsiChar;
  LPChar     : PChar;
  LString    : string;
begin
  LPAnsiChar := 'ABC日本語文字列';

  LString := String(LPAnsiChar);
  LPChar  := PChar(LString);

  MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PAnsiChar 型の文字列を PChar 型の文字列に変換
//  String でキャストして更にPChar でキャスト
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LPAnsiChar : PAnsiChar;
  LPChar     : PChar;
begin
  LPAnsiChar := 'ABC日本語文字列';

  LPChar  := PChar(String(LPAnsiChar));

  MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
end;

//=============================================================================
//  PAnsiChar 型の文字列を PChar 型の文字列に変換
//
//  String 型の文字列に変換後,
//  PChar の文字列のメモリ領域を確保して StrPCopy で文字列のデータをコピー
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LPAnsiChar : PAnsiChar;
  LPChar     : PChar;
  LString    : string;
begin
  LPAnsiChar := 'ABC日本語文字列';

  LString := String(LPAnsiChar);

  LPChar := AllocMem((Length(LString) + 1) * SizeOf(Char));
  try
    StrPCopy(LPChar, LString);
    MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
  finally
    FreeMem(LPChar);
  end;
end;


StrAlloc 関数を使用すると,文字数の指定でメモリ領域の確保ができます.StrAlloc 関数で確保したメモリは StrDispose 関数で解放します.



リスト14
procedure TForm1.Button3Click(Sender: TObject);
var
  LPAnsiChar : PAnsiChar;
  LPChar     : PChar;
  LString    : string;
begin
  LPAnsiChar := 'ABC日本語文字列';

  LString := String(LPAnsiChar);

  LPChar := StrAlloc(Length(LString) + 1);
  try
    StrPCopy(LPChar, LString);
    MessageBox(Handle, LPChar, '結果', MB_ICONINFORMATION);
  finally
    StrDispose(LPChar);
  end;
end;




11_AnsiString 型の文字列における文字単位の処理


AnsiString 型の文字列を文字単位で操作する手順は,Ansi 版の Delphi の時と同じように,1 バイト文字か 2 バイト文字かの判定が必要です.



図12
AnsiString 型の文字列における文字単位の処理例

リスト15
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  function AnsiProessSingleChar(const SrcStr: AnsiString;
    var DstStr: Ansistring): Integer;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  AnsiString 型の文字列を 1 文字単位で処理する関数のテスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LAnsiText   : AnsiString;
  LResAnsiStr : AnsiString;
begin
  LAnsiText := 'ABC日本語文字列';

  AnsiProessSingleChar(LAnsiText, LResAnsiStr);

  Memo1.Lines.Clear;
  Memo1.Lines.Add(LAnsiText);
  Memo1.Lines.Add(LResAnsiStr);
end;

//-----------------------------------------------------------------------------
//  AnsiString 型の文字列を 1 文字単位で処理する関数の例
//  Ansi 版の Delphi の時と同じように 2 バイト文字の判定の処理が必要
//-----------------------------------------------------------------------------
function AnsiProessSingleChar(const SrcStr: AnsiString;
  var DstStr: Ansistring): Integer;
var
  LAnsiString : AnsiString;
  LIndex      : Integer;
begin
  Result := Length(SrcStr);
  if Result = 0 then Exit;

  DstStr := '';

  LIndex := 1;
  while LIndex <= (Length(SrcStr)) do begin
    LAnsiString := '';
    if SrcStr[LIndex] in LeadBytes then begin
      SetLength(LAnsiString, 2);
      Move(SrcStr[LIndex], LAnsiString[1], 2);
      Inc(LIndex, 2);
    end else begin
      LAnsiString := SrcStr[LIndex];
      Inc(LIndex);
    end;

    DstStr := DstStr + LAnsiString + LAnsiString;
  end;
  Result := Length(DstStr);
end;

end.




12_PAnsiChar 型の文字列における文字単位の処理


PAnsiChar 型の文字列を文字単位で操作する手順は,Ansi 版の Delphi の時と同じように,1 バイト文字か 2 バイト文字かの判定が必要です.



図13
PAnsiChar 型の文字列における文字単位の処理例

リスト16
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  function PAnsiProessSingleChar(const SrcPChar: PAnsiChar;
    var DstStr: Ansistring): Integer;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  PAnsiChar 型の文字列を 1 文字単位で処理する関数のテスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPAnsiChar  : PAnsiChar;
  LResAnsiStr : AnsiString;
begin
  LPAnsiChar := '日本語ABC文字列';

  PAnsiProessSingleChar(LPAnsiChar, LResAnsiStr);

  Memo1.Lines.Clear;
  Memo1.Lines.Add(LPAnsiChar);
  Memo1.Lines.Add(LResAnsiStr);
end;

//-----------------------------------------------------------------------------
//  PAnsiChar 型の文字列を 1 文字単位で処理する関数の例
//  Ansi 版の Delphi の時と同じように 2 バイト文字の判定の処理が必要
//-----------------------------------------------------------------------------
function PAnsiProessSingleChar(const SrcPChar: PAnsiChar;
  var DstStr: Ansistring): Integer;
var
  LPAnsiChar  : PAnsiChar;
  LAnsiString : AnsiString;
begin
  Result := Length(SrcPChar);
  if Result = 0 then Exit;

  LPAnsiChar := SrcPChar;
  DstStr := '';

  while byte(LPAnsiChar^) <> 0 do begin
    LAnsiString := '';
    if AnsiChar(LPAnsiChar^) in LeadBytes then begin
      SetLength(LAnsiString, 2);
      Move(LPAnsiChar^, LAnsiString[1], 2);
      Inc(LPAnsiChar, 2);
    end else begin
      LAnsiString := LPAnsiChar^;
      Inc(LPAnsiChar);
    end;

    DstStr := DstStr + LAnsiString + LAnsiString;
  end;
  Result := Length(DstStr);
end;

end.




13_[dcc32 警告] データ損失の可能性がある文字列の暗黙のキャスト


本ページのサンプルをコンパイルすると,ほとんどのサンプルで [dcc32 警告] が現れます.
[データの損失の可能性がある] というのは,例えば String 型から AnsiString 型に変換する時に表示されます.これは,Ansi 文字,つまり Shift-JIS にはない文字が String 型の文字列に含まれている場合等,その文字のデータが損失する可能性があるという意味です.詳細は以下の参考リンクの記事で確認してください.



図14
警告

この警告は,次のよう明示的にキャストすれば現れません.この場合は指定した型で変換されます.


  // W1057 
  // 文字列の暗黙のキャスト ('AnsiString' から 'string') 
  // を非表示にする
  LStrText  := LAnsiText;
     
  LStrText  := string(LAnsiText);

  // W1058 
  // データ損失の可能性がある文字列の暗黙のキャスト ('string' から 'AnsiString')
  // を非表示にする
  LAnsiText := LStrText;
     
  LAnsiText := AnsiString(LStrText);


以下のコンパイラ指令をユニットの先頭に追加すれば警告を表示しないようにできます.

{$WARNINGS OFF}