Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2018年05月31日] [更新 2018年07月21日] Delphi 一般・その他
   
Char 型の文字配列と,String 型または PChar 型への変換
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32
[整理番号 - 0760]







Char 型の文字配列と,String 型または PChar 型への変換


Delphi で使用する String 型の文字列と Null 終端文字列の記事です.それらの文字列の操作ではなく,Char 型の文字配列と String 型,PChar 型の文字列との変換に関する記事です.
Char 型の文字配列は以下のような定義の配列のことです.


    TCharArray
    array of Char
    array [0..MAX_PATH - 1] of Char

Char は 1 文字の型です.Char 型の配列は複数の文字を格納する配列です.どちらも「文字」だけを格納します.一方,Delphi の String 型は文字だけでなく,文字列の情報も格納します.



[備考 1]
String 型の文字列の文字を Char 型の配列にコピーする際,Char 型の配列のサイズを 1 文字分だけ大きくしているサンプルがあります.これは,Char 型の配列に格納した文字を PChar 型の文字列にしたり PChar でキャストする時に必要です.Char 型の文字配列に文字を格納するのに 1 文字分大きいサイズが必要なわけではありません.


[備考 2]
本ページのサンプルでは Char 型の動的配列に array of Char を使用していますが,TCharArray を使用しても結果は同じです.これらの違いは型宣言ありと型宣言なしの違いです.一般的には型宣言があった方が使い勝手はいいと言えますが,本ページのサンプルでは型宣言の利点を利用していませんからどちらでも同じです.
Delphi ではクラスやオブジェクトの型名のプリフィックス (先頭の文字) として,大文字の T を付加する慣習となっています.



本ページのサンプルでは下図のようなフォームを作成して,このフォームを使用するプロジェクトをコピーしてテストしています.



図1
本ページのテストで使用しているフォーム




01_Char 型の文字配列の要素数と文字数と Null 文字


本ページで扱っている Null 文字 (#0) は,改行文字や TAB 文字と同じように,視覚的に文字として認識されない「文字」です.ASCII 制御文字 (アスキー制御文字) と呼ばれることもあります.

したがって,Char 型の配列に文字が何も代入されていない状態で文字列として取得しても,その文字列の長さは 0 です.Char 型の配列の 2 番目以降の要素に文字が格納されていても,それより前の要素の全てに意味のある文字がなければ,その Char 型の配列を文字列にしても空文字となります.文字列として取得する際,Null 文字を文字列の終端の判定に使用するからです.

本項のサンプルはこのことを確認するためのコードです.
配列の要素数は Length 関数で取得できます.また,配列に格納された文字数は,Char 型の静的配列であれば StrLen 関数で取得することができます.
以下は Char 型の文字配列が静的な配列の場合です.



[備考]
#0 はコードポイント (文字コード) の値が 0 (ゼロ) である文字を意味します.$0 と書くと 16 進数で表示した数値の 0 (ゼロ) を意味します.単に 0 と書くと数値のゼロです.
例えば以下を実行するとどちらも'東'という文字を表示します.東という文字のコードポイントの値は 10 進数では 26481 で,16 進数では 6771 だからです.
    ShowMessage(#26481);
    ShowMessage(#$6771);
当然ですが,
    ShowMessage(#0);
を実行しても何の文字も表示しません.


図2
配列要素に文字が格納されていない時
  • 要素数は設定した値
  • 文字数は 0
図3
配列の 2 番目の要素に文字を格納した結果
  • 配列の最初の要素が Null 文字なので文字列としては認識してくれない
  • 要素数は設定した値
  • 文字数は 0
図4
配列の 1 番目の要素だけに文字を格納した結果
  • 要素数は設定した値
  • 文字数は 1

リスト1
//=============================================================================
//  Char型の文字配列のエレメント数と文字数
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LpBuffer : array [0..50] of Char;
  LLength  : Integer;
  LStrLen  : Integer;
begin
  Memo1.Lines.Clear;

  FillChar(LpBuffer, SizeOf(LpBuffer), #0);

  LLength := Length(LpBuffer);
  LStrLen := StrLen(LpBuffer);

  Memo1.Lines.Add(LpBuffer);
  Memo1.Lines.Add(IntToStr(LLength));
  Memo1.Lines.Add(IntToStr(LStrLen));
end;

//=============================================================================
//  Char型の文字配列のエレメント数と文字数
//  最初の要素の文字がNull文字(#0)だと文字列としては取得できない
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LpBuffer : array [0..50] of Char;
  LLength  : Integer;
  LStrLen  : Integer;
begin
  Memo1.Lines.Clear;

  FillChar(LpBuffer, SizeOf(LpBuffer), #0);
  LpBuffer[1] := '東';

  LLength := Length(LpBuffer);
  LStrLen := StrLen(LpBuffer);

  Memo1.Lines.Add(LpBuffer);
  Memo1.Lines.Add(IntToStr(LLength));
  Memo1.Lines.Add(IntToStr(LStrLen));
end;

//=============================================================================
//  Char型の文字配列のエレメント数と文字数
//  最初の要素に文字が格納されていればNull文字(#0)までが文字列として取得できる
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LpBuffer : array [0..50] of Char;
  LLength  : Integer;
  LStrLen  : Integer;
begin
  Memo1.Lines.Clear;

  FillChar(LpBuffer, SizeOf(LpBuffer), #0);
  LpBuffer[0] := '東';

  LLength := Length(LpBuffer);
  LStrLen := StrLen(LpBuffer);

  Memo1.Lines.Add(LpBuffer);
  Memo1.Lines.Add(IntToStr(LLength));
  Memo1.Lines.Add(IntToStr(LStrLen));
end;


上のサンプルは Char 型の静的配列の場合ですが,次は Char 型の動的配列の場合です.Char 型の動的配列の文字数は,PChar でキャストすれば StrLen 関数で取得できます.
動的配列の 1 番目の要素のインデックス (番号) は常に 0 です.



リスト2
//=============================================================================
//  Char型の文字配列のエレメント数と文字数
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LpBuffer : TCharArray;
  LLength  : Integer;
  LStrLen  : Integer;
begin
  Memo1.Lines.Clear;

  SetLength(LpBuffer, 50);

  LLength := Length(LpBuffer);
  LStrLen := StrLen(PChar(LpBuffer));

  Memo1.Lines.Add(string(LpBuffer));
  Memo1.Lines.Add(IntToStr(LLength));
  Memo1.Lines.Add(IntToStr(LStrLen));
end;

//=============================================================================
//  Char型の文字配列のエレメント数と文字数
//  最初の要素の文字がNull文字(#0)だと文字列としては取得できない
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LpBuffer : TCharArray;
  LLength  : Integer;
  LStrLen  : Integer;
begin
  Memo1.Lines.Clear;

  SetLength(LpBuffer, 50);

  LpBuffer[1] := '東';

  LLength := Length(LpBuffer);
  LStrLen := StrLen(PChar(LpBuffer));

  Memo1.Lines.Add(string(LpBuffer));
  Memo1.Lines.Add(IntToStr(LLength));
  Memo1.Lines.Add(IntToStr(LStrLen));
end;

//=============================================================================
//  Char型の文字配列のエレメント数と文字数
//  最初の要素に文字が格納されていればNull文字(#0)までが文字列として取得できる
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LpBuffer : TCharArray;
  LLength  : Integer;
  LStrLen  : Integer;
begin
  Memo1.Lines.Clear;

  SetLength(LpBuffer, 50);

  LpBuffer[0] := '東';

  LLength := Length(LpBuffer);
  LStrLen := StrLen(PChar(LpBuffer));

  Memo1.Lines.Add(string(LpBuffer));
  Memo1.Lines.Add(IntToStr(LLength));
  Memo1.Lines.Add(IntToStr(LStrLen));
end;




02_Char 型の文字配列要素の初期化


Char 型の静的配列要素の値は,前項のサンプルで使用している FillChar 手続きで Null 文字に初期化することができます.この初期化を行わないと,PChar 型の文字列や String 型の文字列に代入した時に「ゴミ文字」が入ることがあります.
この「ゴミ文字」は,前項のサンプルコードだけの単純なプログラムであれば,ほとんど入る可能性はありません.

String 型の変数等を Button1 クリックのイベント内や関数内でローカル変数として定義すると,その変数等が使用したメモリは,そのイベントや関数の実行が終了すると (スコープを抜けると) 解放されます.それらの解放されたメモリの内容は初期化されるわけではありません.

別のスコープで定義した変数類がそれらのメモリ領域を使用することがあります.これは避けることはできません.Char 型の配列の変数も例外ではありません.それらのメモリの内容が配列要素にあればゴミとなる可能性があります.

Char 型の動的配列の場合は,SetLength 関数を実行してメモリを確保した時に初期化されます.






03_String 型の文字列を Char 型の配列にコピー


Windows API の関数の引数に文字列を使用する際,PChar 型ではなく,Char 型の配列を使用すると便利で効率がいい場合があります.そのためには String 型の文字列を Char 型の配列に格納する必要があります.StrPCopy 関数を使用すると String 型の各文字を Char 型の配列の各要素に格納してくれます.StrPCopy 関数は Null 終端文字を自動で追加しますが,その分の要素数が確保されている必要があります.
もちろん 1 文字づつ Char 型の配列要素に代入していくこともできます.

以下は Char 型の配列が静的配列の場合です.
@LpBuffer[0] は配列要素が 0 のポインタである LpBuffer[0] のアドレスを意味しています.この記述の方が正確ですが,以下のいずれの記述でも変換できます.Char 型の静的配列の要素が 0 以外,例えば 1 から始まる場合は @LpBuffer[1] のように指定する必要があります.


  StrPCopy(@LpBuffer[0], LStrText);
  StrPCopy(LpBuffer, LStrText);


図5
String 型の文字列を Char 型の配列に代入
  • 出力先の Char 型配列が静的配列の場合

リスト3
//=============================================================================
//  string型の値をChar型の配列に変換
//  変換先がChar型の静的配列の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrText : string;
  LpBuffer : array [0..MAX_PATH - 1] of Char;
begin
  Memo1.Lines.Clear;

  LStrText :='String 型文字列を Char 型配列に' + sLineBreak + '静的配列';

  //String型のデータをChar型の配列にコピー
  //StrPCopy(@LpBuffer[0], LStrText); でもよい
  //StrPCopy(LpBuffer, LStrText);でもよい
  StrPCopy(Pchar(@LpBuffer[0]), LStrText);

  //確認表示
  Memo1.Lines.Add(LpBuffer);
end;


出力先の Char 型の配列が動的配列の場合です.静的配列と同じように StrPCopy 関数を使用して配列の要素に文字列をコピーできます.StrPCopy 関数の引数が以下のいずれでも変換できます.
動的配列の要素番号は常に 0 からです.


  StrPCopy(@LpBuffer[0], LStrText);
  StrPCopy(PChar(LpBuffer), LStrText);



図6
String 型の文字列を Char 型の配列に代入
  • 出力先の Char 型配列が動的配列の場合

リスト4
//=============================================================================
//  string型の値をChar型の配列に変換
//  変換先がChar型の動的配列の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrText : string;
  LpBuffer : array of Char;
begin
  Memo1.Lines.Clear;

  LStrText :='String 型文字列を Char 型配列に' + sLineBreak + '動的配列';

  //Char型の動的文字列配列のサイズを設定
  //+1はnull終端文字用
  SetLength(LpBuffer, Length(LStrText) + 1);


  //String型のデータをChar型の配列にコピー
  //StrPCopy(@LpBuffer[0], LStrText);でも変換できる
  //StrPCopy(PChar(LpBuffer), LStrText); でもよい
  StrPCopy(PChar(@LpBuffer[0]), LStrText);

  //確認表示
  //動的配列の場合は明示的に型キャストが必要
  Memo1.Lines.Add(PChar(@LpBuffer[0]));
end;


StrPLCopy 関数を使用すると,コピー元の String 型の文字列を指定文字数だけを Char 型の文字配列に代入することができます.
コピー先の Char 型の文字配列にも指定位置から代入できますが,コピー先の Char 型の文字配列に文字が格納されていないと意味がありません.




図7
String 型の文字列を Char 型の配列に代入
  • 指定文字数だけをコピーした結果

リスト5
//=============================================================================
//  string型の値をChar型の配列にコピー
//  コピー先がChar型の静的配列の場合
//
//  String型の指定文字数だけをコピーする例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrText : string;
  LpBuffer : array [0..MAX_PATH - 1] of Char;
begin
  Memo1.Lines.Clear;

  LStrText :='String 型文字列を Char 型配列に' + sLineBreak + '静的配列';

  //String型のデータをChar型の配列にコピー
  StrPLCopy(Pchar(@LpBuffer[0]), LStrText, 11);

  //確認表示
  Memo1.Lines.Add(LpBuffer);
end;


次のコードは出力先の Char 型の配列が動的配列の場合ですが,その配列が型宣言された動的な配列の場合は ToCharArray メソッドで String 型の文字列を Char 型の配列にコピーできます.
TCharArray は StringHelper のメソッドです.StringHelper は Delphi XE3 で実装されました.




図8
String 型の文字列を Char 型の配列に代入
  • 出力先の Char 型配列が型宣言の動的配列の場合
  • StringHelper の ToCharArray メソッドを使用して変換できる

リスト6
//=============================================================================
//  string型の値をChar型の配列に変換
//  変換先がChar型の型宣言の動的配列の場合
//
//  動的なChar型の配列への変換には,StringHelperのToCharArrayが使用可能
//  以下の型宣言なしの動的配列には使用できない
//  LpBuffer : array of Char;
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrText : string;
  LpBuffer : TCharArray;
begin
  Memo1.Lines.Clear;

  LStrText :='String 型文字列を Char 型配列に' + sLineBreak
            + '型宣言の動的配列 - ToCharArray メソッド';

  //String型の文字列をChar型の文字配列に代入
  LpBuffer := LStrText.ToCharArray;

  //確認表示
  //動的配列の場合は明示的に型キャストが必要
  //PCharでキャストすると末尾にゴミが表示される
  Memo1.Lines.Add(string(@LpBuffer[0]));
end;


文字単位で 1 文字づつループで処理する例です.
ループで処理すると代入の際にいろいろな操作が可能です.状況によってはこのように 1 文字単位で処理する方が効率的な場合もあります.




図9
String 型の文字列を Char 型の配列に代入
  • ループで要素単位で処理

リスト7
//=============================================================================
//  string型の値をChar型の配列に代入する例
//  1文字づつ処理する例
//
//  String型の最初の文字は要素番号1にふることに注意
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrText : string;
  LpBuffer : array of Char;
  LIndex   : Integer;
begin
  Memo1.Lines.Clear;

  LStrText :='String 型文字列を Char 型配列に' + sLineBreak + 'ループ処理で代入';

  //Char型の動的文字列配列のサイズを設定
  //+1はnull終端文字用
  SetLength(LpBuffer, Length(LStrText) + 1);

  //String型の文字列をChar型の配列に代入
  for LIndex := 0 to LStrText.Length - 1 do begin
    LpBuffer[LIndex] := LStrText[LIndex + 1];
  end;

  //確認表示
  //動的配列の場合は明示的に型変キャストが必要
  Memo1.Lines.Add(PChar(@LpBuffer[0]));
end;




04_Char 型の配列データを String 型の文字列にコピー


Char 型の文字配列の文字を String 型の文字列にコピーするサンプルです.
以下はコピー元 の Char 型の配列が静的配列の場合です.Char 型の配列の変数を String 型の変数に代入するだけです.



図10
Char 型配列の文字を String 型の変数ににコピー
  • コピー元の Char 型配列が静的配列の場合
  • 文字数には改行文字も含まれている

リスト8
//=============================================================================
//  Char型の文字配列の値をString型の値に変換
//  静的配列の例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSrcStr    : string;
  LCharArray : array [0..MAX_PATH - 1] of Char;
  LText      : string;
begin
  Memo1.Lines.Clear;

  LSrcStr := 'Char 型配列の文字を String 型の文字列に' + sLineBreak + '静的配列';

  //テスト用のChar型の文字配列を作成
  StrPCopy(Pchar(@LCharArray[0]), LSrcStr);


  //String型の変数に代入
  LText := LCharArray;

  //確認表示
  //文字列の長さも表示してみる
  Memo1.Lines.Add('要素数 : ' +  IntToStr(Length(LCharArray)));
  Memo1.Lines.Add('文字数 : ' +  IntToStr(LText.Length));
  Memo1.Lines.Add('文字数 : ' +  IntToStr(StrLen(LCharArray)));
  Memo1.Lines.Add(LText);
end;


次のように明示的に PChar で型キャストすることも,任意の位置以降の文字だけを String 型の変数にコピーすることもできます.
静的配列の場合,要素の最初の番号が 0 以外の時があります.その時は以下のように明示的なキャストが必要となります



図11
Char 型配列の文字を String 型の変数ににコピー
  • コピー元の Char 型配列の 10 文字目以降からコピーした結果

  //PChar型で明示的にキャストする場合
  LText := PChar(@LCharArray[0]);

  //10文字目以降だけをコピーする場合
  LText := PChar(@LCharArray[9]);


同じく Char 型の文字配列の文字を String 型の文字列にコピーするサンプルです.
コピー元 の Char 型の配列が動的配列の場合です.Char 型の配列の先頭要素のアドレスを PChar で型キャストすれば String 型の変数に代入できます.動的配列の先頭要素の番号は常に 0 です.

このサンプルでは Char 型の文字配列が確保した要素数は 33 です.それを String で型キャストした文字列の文字数は Null 終端文字の分も含んだ 33 になっています.Trim 関数で Null 終端文字を削除すると文字数は 32 となります.
PChar 型の文字列の長さには Null 終端文字の分は含まれません.Nulll 終端文字は PChar 型の文字列では文字列の終端を意味する「記号」として使われるるだけです.



図12
Char 型配列の文字を String 型の変数ににコピー
  • コピー元の Char 型配列が動的配列の場合

リスト9
//=============================================================================
//  Char型の文字配列の値をString型の値に変換
//  動的配列の例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSrcStr    : string;
  LCharArray : array of Char;
  LText      : string;
  LTextPChar : string;
begin
  Memo1.Lines.Clear;

  LSrcStr := 'Char 型配列の文字を String 型の文字列に' + sLineBreak + '動的配列';

  //テスト用のChar型の文字配列を作成
  SetLength(LCharArray, LSrcStr.Length + 1);
  StrPCopy(@LCharArray[0], LSrcStr);


  //String型の変数に代入
  //動的配列の場合は明示的にキャストする必要がある
  LText      := string(LCharArray);
  LTextPChar := PChar(LCharArray);

  //確認表示
  //文字列の長さも表示してみる
  Memo1.Lines.Add('要素数 : ' +  IntToStr(Length(LCharArray)));
  Memo1.Lines.Add('文字数 : ' +  IntToStr(LText.Length));
  Memo1.Lines.Add('文字数 : ' +  IntToStr(Trim(LText).Length));
  Memo1.Lines.Add('文字数 : ' +  IntToStr(LTextPChar.Length));
  Memo1.Lines.Add('文字数 : ' +  IntToStr(StrLen(PChar(LCharArray))));
  Memo1.Lines.Add(LText);
end;




05_Char 型の文字配列のデータを PChar 型の文字列にコピー


Char 型の文字配列の文字を PChar 型の文字列にコピーするサンプルです.
以下はコピー元 の Char 型の配列が静的配列の場合です.Char 型の配列の変数を PChar 型の変数に代入するだけです.



図13
Char 型配列の文字を PChar 型の変数ににコピー
  • コピー元の Char 型配列が静的配列の場合

リスト10
//=============================================================================
//  Char型の文字配列をPChar型の文字列に
//  Char型の文字配列が静的配列の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText    : string;
  LCharArr : array [0..MAX_PATH - 1] of Char;
  LPChar   : PChar;
begin
  Memo1.Lines.Clear;

  LText := 'Char 型の文字配列を PChar 型に';

  //Null文字(#0)で初期化しないとゴミが入ることがある
  FillChar(LCharArr, SizeOf(LCharArr), #0);
  StrPCopy(LCharArr, LText);


  //実際の変換
  //Char型の文字配列の値をPChar型の文字列に変換
  LPChar := LCharArr;

  //確認表示
  Memo1.Lines.Add('文字数 : ' + IntToStr(Length(LText)));
  Memo1.Lines.Add('文字数 : ' + IntToStr(StrLen(LPChar)));
  Memo1.Lines.Add(LPChar);
end;


次のように明示的に PChar で型キャストすることも,任意の位置以降の文字だけを PChar 型の変数にコピーすることもできます.
静的配列の場合,要素の最初の番号が 0 以外の時があります.その時は以下のように明示的なキャストが必要となります.



図14
Char 型配列の文字を PChar 型の変数ににコピー
  • コピー元の Char 型配列の 10 文字目以降からコピーした結果

  //PChar型で明示的にキャストする場合
  LPChar := PChar(@LCharArray[0]);

  //10文字目以降だけをコピーする場合
  LPChar := PChar(@LCharArray[9]);


同じく Char 型の文字配列の文字を PChar 型の文字列にコピーするサンプルです.
コピー元 の Char 型の配列が動的配列の場合です.Char 型の配列の先頭要素のアドレスを PChar で型キャストすれば PChar 型の変数に代入できます.動的配列の先頭要素の番号は常に 0 です.



図15
Char 型配列の文字を PChar 型の変数ににコピー
  • コピー元の Char 型配列が動的配列の場合

リスト11
//=============================================================================
//  Char型の文字配列をPChar型の文字列に
//  Char型の文字配列が動的配列の場合
//
//  PChar型の変数にChar型配列の先頭アドレスを代入する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText    : string;
  LCharArr : array of Char;
  LPChar   : PChar;
begin
  Memo1.Lines.Clear;

  LText := 'Char 型の文字配列を PChar 型に';

  //Char型の文字配列の要素数を設定(+1はNull終端文字の分)
  SetLength(LCharArr, Length(LText) + 1);
  StrPCopy(@LCharArr[0], LText);


  //実際の変換
  //Char型の文字配列のデータをPChar型の文字列とする
  LPChar := @LCharArr[0];

  //確認表示
  Memo1.Lines.Add('文字数 : ' + IntToStr(Length(LText)));
  Memo1.Lines.Add('文字数 : ' + IntToStr(StrLen(LPChar)));
  Memo1.Lines.Add(LPChar);
end;




06_PChar 型の文字列を Char 型の文字配列にコピー


PChar 型の文字列を Char 型の文字配列にコピーするサンプルです.
以下はコピー先の Char 型の配列が静的配列の場合です.StrCopy 関数を使用しています.StrLCopy 関数を使用すると,指定した文字数たけをコピーできます.

以下のいずれの記述でも変換できます.Char 型の静的配列の要素が 0 以外,例えば 1 から始まる場合は @LCharArr[1] のように指定する必要があります.


  StrCopy(@LCharArr[0], LPChar);
  StrCopy(PChar(@LCharArr[0]), LPChar);



図16
PChar 型の文字列を Char 型の文字配列にコピー
  • コピー先が Char 型の静的配列の場合

リスト12
//=============================================================================
//  PChar型の文字列をChar型の文字配列にコピー
//  StrCopy関数を使用した変換
//
//  コピー先がChar型の静的配列の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPChar   : PChar;
  LCharArr : array [0..MAX_PATH - 1] of Char;
begin
  Memo1.Lines.Clear;

  //テスト用の文字配列を作成
  LPChar := 'PChar 型の文字列を Char 型の文字配列に';

  //実際の変換
  //Null文字(#0)で初期化しないとゴミが入ることがある
  FillChar(LCharArr, SizeOf(LCharArr), #0);
  StrCopy(LCharArr, LPChar);

  //確認表示
  Memo1.Lines.Add('文字数 : ' + IntToStr(StrLen(LCharArr)));
  Memo1.Lines.Add('要素数 : ' + IntToStr(Length(LCharArr)));
  Memo1.Lines.Add(LCharArr);
  Memo1.Lines.Add('静的配列');
end;

同じく PChar 型の文字列を Char 型の文字配列にコピーするサンプルです.
コピー先 の Char 型の配列が動的配列の場合です.動的配列の先頭要素の番号は常に 0 です.



図17
PChar 型の文字列を Char 型の文字配列にコピー
  • コピー先が Char 型の動的配列の場合

リスト13
//=============================================================================
//  PChar型の文字列をChar型の文字配列にコピー
//  StrCopy関数を使用した変換
//
//  コピー先がChar型の動的配列の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPChar   : PChar;
  LCharArr : array of Char;
  LText    : string;
begin
  Memo1.Lines.Clear;

  //テスト用の文字配列を作成
  LPChar := 'PChar 型の文字列を Char 型の文字配列に';

  //実際の変換
  //配列の要素数を設定(+1はNull終端文字分)
  SetLength(LCharArr, StrLen(LPChar) + 1);
  StrCopy(@LCharArr[0], LPChar);

  //確認表示
  LText := PChar(LCharArr);
  Memo1.Lines.Add('文字数 : ' + IntToStr(StrLen(PChar(@LCharArr[0]))));
  Memo1.Lines.Add('要素数 : ' + IntToStr(Length(LCharArr)));
  Memo1.Lines.Add(LText);
  Memo1.Lines.Add('動的配列');
end;




07_MoveChars 関数による Char 型の配列等の文字のコピー


MoveChars 関数を使用すると,コピー元の文字例の任意の位置から指定した文字数を別の Chaar 型の文字配列等にコピーできます.コピー先のコピー位置も指定できます.部分文字列のコピーと置換を同時に行う関数とも言えます.
Move となっていますが動作は「複写」です.移動はしません.



[備考]
2018年07月02日現在の MoveChars 関数のオンラインヘルプでは,第 3 引数の単位がバイトとなっていますが,実際には文字数です.バイト単位の指定は Move 関数の方です.


Char 型の文字配列の文字を別の Char 型の文字配列にコピーする例です.Char 型の文字配列が静的配列の場合です.




図18
Char 型の文字配列の文字を,別の Char 型の文字配列にコピー
  • コピー元の 'テスト' という文字だけをコピー先の Char 型の文字配列にコピーした結果

リスト14
//=============================================================================
//  MoveChars関数による文字のコピー
//  Char型の文字配列の文字を別のChar型の文字配列の指定位置へコピーする例
//
//  Char型の配列が静的配列の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LString     : string;
  LCharArrSrc : array [0..99] of Char;
  LCharArrDst : array [0..99] of Char;
  LCopyCnt    : Integer;
begin
  Memo1.Lines.Clear;

  //テスト用の文字列を作成
  LString := '東西南北テスト文字列';
  StrPCopy(@LCharArrSrc[0], LString);

  LString := '123456789';
  StrPCopy(@LCharArrDst[0], LString);

  //作成した文字列を確認表示
  Memo1.Lines.Add(LCharArrSrc);
  Memo1.Lines.Add(LCharArrDst);

  //コピー実行
  LCopyCnt := 3;
  MoveChars(LCharArrSrc[4], LCharArrDst[4], LCopyCnt);

  //コピーの結果を表示
  Memo1.Lines.Add('');
  Memo1.Lines.Add(LCharArrDst);
end;


同じく Char 型の配列文字配列の文字を別の Char 型の文字配列にコピーする例ですが,Char 型の文字配列が動的配列の場合です.



リスト15
//=============================================================================
//  MoveChars関数による文字のコピー
//  Char型の文字配列の文字を別のChar型の文字配列の指定位置へコピーする例
//
//  Char型の配列が動的配列の場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LString     : string;
  LCharArrSrc : array of Char;
  LCharArrDst : array of Char;
  LCopyCnt    : Integer;
begin
  Memo1.Lines.Clear;

  //テスト用の文字列を作成
  LString := '東西南北テスト文字列';
  SetLength(LCharArrSrc, Length(LString) + 1);
  StrPCopy(@LCharArrSrc[0], LString);

  LString := '123456789';
  SetLength(LCharArrDst, Length(LString) + 1);
  StrPCopy(@LCharArrDst[0], LString);

  //作成した文字列を確認表示
  Memo1.Lines.Add(PChar(LCharArrSrc));
  Memo1.Lines.Add(PChar(LCharArrDst));

  //コピー実行
  LCopyCnt := 3;
  MoveChars(LCharArrSrc[4], LCharArrDst[4], LCopyCnt);

  //コピーの結果を表示
  Memo1.Lines.Add('');
  Memo1.Lines.Add(string(LCharArrDst));
end;


String 型の文字列の一部を Char 型の文字配列に位置を指定してコピーする例です.
String 型の文字列の最初の文字の要素番号は 1 ですが,動的配列の最初の要素番号は 0,つまり 0 ベースであることに注意します.




図19
String 型の文字列の一部を Char 型の文字配列に位置指定してコピー
  • '南北' と言う文字だけをコピー

リスト16
//=============================================================================
//  MoveChars関数による文字のコピー
//  String型の文字列の部分文字列をChar型の文字配列の指定位置へコピーする例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrSrc     : string;
  LStrDst     : string;
  LCharArrDst : array of Char;
  LCopyCnt    : Integer;
begin
  Memo1.Lines.Clear;

  //テスト用の文字列
  LStrSrc := '東西南北テスト文字列';

  LStrDst := '123456789';
  SetLength(LCharArrDst, Length(LStrDst) + 1);
  StrPCopy(@LCharArrDst[0], LStrDst);

  //作成した文字列を確認表示
  Memo1.Lines.Add(LStrSrc);
  Memo1.Lines.Add(PChar(LCharArrDst));

  //コピー実行
  LCopyCnt := 2;
  MoveChars(LStrSrc[3], LCharArrDst[4], LCopyCnt);

  //コピーの結果を表示
  Memo1.Lines.Add('');
  Memo1.Lines.Add(string(LCharArrDst));
end;


PChar 型の文字列の一部を Char 型の文字配列に位置を指定してコピーする例です.
PChar 型の文字列は,String でキャストすれば文字の位置を指定できます.




図20
PChar 型の文字列の一部を Char 型の文字配列に位置指定してコピー
  • '東西南北' という文字だけをコピー

リスト17
//=============================================================================
//  MoveChars関数による文字のコピー
//  PChar型の文字列の部分文字列をChar型の文字配列の指定位置へコピーする例
//
//  PChar型の文字列はstring型でキャストすると操作できる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPCharSrc   : PChar;
  LStrDst     : string;
  LCharArrDst : array of Char;
  LCopyCnt    : Integer;
begin
  Memo1.Lines.Clear;

  //テスト用の文字列
  LPCharSrc := '東西南北テスト文字列';

  LStrDst := '123456789';
  SetLength(LCharArrDst, Length(LStrDst) + 1);
  StrPCopy(@LCharArrDst[0], LStrDst);

  //作成した文字列を確認表示
  Memo1.Lines.Add(LPCharSrc);
  Memo1.Lines.Add(PChar(LCharArrDst));

  //コピー実行
  LCopyCnt := 4;
  MoveChars(string(LPCharSrc)[1], LCharArrDst[3], LCopyCnt);

  //コピーの結果を表示
  Memo1.Lines.Add('');
  Memo1.Lines.Add(string(LCharArrDst));
end;




08_Char 型の配列を String と PChar でキャストする時の違い


これまでのサンプルで,Char 型の配列に格納されている文字を String 型の文字列に代入する際,以下のように String または PChar でキャストしているコードがあります.以下はこの両者の動作の違いについて確認するテストコードです.


  //String型の変数に代入
  LText := string(LCharArray);
  LText := PChar(LCharArray);


Char 型の文字配列の文字を String でキャストして String 型の文字列変数に代入する例です.
文字列変数に代入した後,代入元の Char 型の配列要素の文字を変更します.そして代入先の文字列を再度表示していますが,代入先の文字列の内容も変化しています.
本ページのサンプルでは,最終的な結果としての表示だけにこのようなキャストを使用しています.したがってサンプルとしての動作に問題はありません.



[備考]
ここまでのサンプルではテスト用の Char 型の文字配列データを作成するのに主に StrPCopy 関数を使用してきました.本項のサンプルでは MoveChars 関数を使用しています.
Char 型の文字配列に限って言えば,StrPCopy 関数や StrCopy 関数よりも MoveChars 関数方が扱い易いかも知れません.



図21
Char 型の文字配列の文字データを String でキャストして String 型の文字列変数に代入

リスト18
//=============================================================================
//  Char型の配列データをString型の文字列に代入
//  stringでキャストする場合
//
//  TCharArrayはChar型の動的配列
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSrcStr    : string;
  LDstStr    : string;
  LCharArray : TCharArray;
begin
  Memo1.Lines.Clear;

  LSrcStr := '日本語 ABC 文字列';

  //テスト用のChar型の文字配列データを作成
  SetLength(LCharArray, Length(LSrcStr));
  MoveChars(LSrcStr[1], LCharArray[0], Length(LSrcStr));


  //String型の変数に代入
  LDstStr := string(LCharArray);

  //確認表示
  Memo1.Lines.Add(LDstStr);

  //代入元の文字配列の要素の値を変更してみる
  LCharArray[0] := '東';
  LCharArray[1] := '京';
  LCharArray[2] := '都';
  Memo1.Lines.Add(LDstStr);
  Memo1.Lines.Add('文字数 : ' + IntToStr(Length(LDstStr)));
end;


Char 型の文字配列の文字を PChar でキャストして String 型の文字列変数に代入する例です.
文字列変数に代入した後,代入元の Char 型の配列要素の文字を変更します.そして代入先の文字列を再度表示していますが,上のサンプルとは違い,代入先の文字列の内容は変化しません.元の Char 型配列と代入先の String 型の文字列は独立した領域に格納されます.

一般的に String 型の変数どうし,PChar 型の変数どうし,あるいは String 型と PChar 型の変数との代入では,同じスレッド内の独立した領域に格納されます.

このサンプルでは Char 型の配列を PChar でキャストするため,元の Char 型の配列の末尾には Null 文字が必要です.PChar 型の文字列は Null 文字の前までの文字を文字列として扱います.



図22
Char 型の文字配列の文字データを PChar でキャストして String 型の文字列変数に代入

リスト19
//=============================================================================
//  Char型の配列データをString型の文字列に代入
//  PCharでキャストする場合
//  変換元のChar型配列のデータにNull文字が必要
//
//  TCharArrayはChar型の動的配列
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSrcStr    : string;
  LDstStr    : string;
  LCharArray : TCharArray;
begin
  Memo1.Lines.Clear;

  LSrcStr := '日本語 ABC 文字列';

  //テスト用のChar型の文字配列データを作成
  //後でPChar型でキャストするのでNull文字分が必要
  SetLength(LCharArray, Length(LSrcStr) + 1);
  MoveChars(LSrcStr[1], LCharArray[0], Length(LSrcStr));


  //String型の変数に代入
  LDstStr := PChar(LCharArray);

  //確認表示
  Memo1.Lines.Add(LDstStr);

  //代入元の文字配列の要素の値を変更してみる
  LCharArray[0] := '東';
  LCharArray[1] := '京';
  LCharArray[2] := '都';
  Memo1.Lines.Add(LDstStr);
  Memo1.Lines.Add('文字数 : ' + IntToStr(Length(LDstStr)));
end;


次のように,MoveChars 関数を使用して Char 型配列のデータを String 型の変数のデータとしてコピーすれば間違いなく独立した領域にコピーされます.
このサンプルでは PChar 型へのキャストはしていないので,元の Char 型配列の要素数として文字列の文字数だけを確保しています. Null 文字の分を確保する必要はありません.



図23
Char 型の文字配列の文字データを MoveChars 関数を使用して String 型の文字列にコピー

リスト20
//=============================================================================
//  Char型の配列データをString型の文字列に代入
//  MoveChars関数でコピーする場合
//
//  TCharArrayはChar型の動的配列
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LSrcStr    : string;
  LDstStr    : string;
  LCharArray : TCharArray;
begin
  Memo1.Lines.Clear;

  LSrcStr := '日本語 ABC 文字列';

  //テスト用のChar型の文字配列データを作成
  SetLength(LCharArray, Length(LSrcStr));
  MoveChars(LSrcStr[1], LCharArray[0], Length(LSrcStr));


  //String型の変数のサイズを設定
  SetLength(LDstStr, Length(LCharArray));
  //Char型配列のデータをString型の変数にコピー
  MoveChars(LCharArray[0], LDstStr[1], Length(LCharArray));

  //確認表示
  Memo1.Lines.Add(LDstStr);

  //代入元の文字配列の要素の値を変更してみる
  LCharArray[0] := '東';
  LCharArray[1] := '京';
  LCharArray[2] := '都';
  Memo1.Lines.Add(LDstStr);
  Memo1.Lines.Add('文字数 : ' + IntToStr(Length(LDstStr)));
end;




09_PChar 型や Char 型の文字配列を関数や手続きの引数に使用


Char 型の文字配列を引数に持つ関数の例です.
以下は Char 型の配列が静的配列の場合です.Char 型の配列を関数や手続きの引数にする場合は,型宣言した配列を使用した方が便利です.このサンプルも型宣言した配列変数を使用しています.

PChar 型の変数を引数とする関数や手続きについては,以下の記事に DLL 内の関数のサンプルコードがあります.Char 型の文字配列を引数に渡す例も掲載しています. DLL 内の関数や手続であっても,同じプロジェクト内の関数や手続きであっても考え方は同じです.



図24
Char 型の文字配列の引数を持つ手続きの実行結果

リスト21
implementation

{$R *.DFM}

type
  TArrayChar = array [0..200] of Char;
  PArrayChar = ^TArrayChar;

//-----------------------------------------------------------------------------
//  静的な Char 型の配列を引数に持つ手続きの例
//  Char 型の配列を引数にする場合は型宣言の配列が便利
//-----------------------------------------------------------------------------
procedure TestProc(ACharArray: TArrayChar);
var
  LText : string;
begin
  LText := ACharArray;

  Form1.Memo1.Lines.Add(LText);
  Form1.Memo1.Lines.Add(IntToStr(Length(LText)));
end;

//=============================================================================
//  静的な Char 型の配列を関数や手続きに渡す例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCharArray : TArrayChar;
  LSrcStr    : string;
begin
  Memo1.Lines.Clear;

  LSrcStr := '日本語 ABC 文字列';

  // 初期化
  // Char 型の配列のサイズはコピー元の (文字数 + 1) 以上にする
  FillChar(LCharArray, SizeOf(LCharArray), #0);
  // 文字列を Char 型配列にコピー
  MoveChars(LSrcStr[1], LCharArray[0], Length(LSrcStr));

  // 手続きを実行
  TestProc(LCharArray);
end;


Char 型の配列が動的配列の場合です.型宣言の配列を使用しています.



リスト22
//-----------------------------------------------------------------------------
//  動的な Char 型の配列を引数に持つ手続きの例
//  Char 型の配列を引数にする場合は型宣言の配列が便利
//-----------------------------------------------------------------------------
procedure TestProc(ACharArray: TCharArray);
var
  LText : string;
begin
  LText := PChar(ACharArray);

  Form1.Memo1.Lines.Add(LText);
  Form1.Memo1.Lines.Add(IntToStr(Length(LText)));
end;

//=============================================================================
//  動的な Char 型の配列を関数や手続きに渡す例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCharArray : TCharArray;
  LSrcStr    : string;
begin
  Memo1.Lines.Clear;

  LSrcStr := '日本語 ABC 文字列';

  // Char 型配列のサイズを決定 ( +1 は Null 終端文字分)
  SetLength(LCharArray, Length(LSrcStr) + 1);
  // 文字列を Char 型配列にコピー
  MoveChars(LSrcStr[1], LCharArray[0], Length(LSrcStr));

  // 手続きを実行
  TestProc(LCharArray);
end;