web-dev-qa-db-ja.com

実行時まで長さがわからないときに配列を宣言するにはどうすればよいですか?

元々、グローバル変数として定義された配列[1..1000]がありました。しかし今、私はそれをnではなく1000にする必要があり、後になるまでnを見つけられません。配列を埋める前にnが何であるかは知っていますが、グローバルにする必要があるため、実行時にグローバル配列のサイズを定義する方法が必要です。

コンテキストは、ファイル内のバイトの線形変換で配列を埋めています。誰かがそれを開きたいと思うまでファイルの大きさがわからないし、ファイルはどんなサイズでもかまいません。

34
Arthur

Delphi 4の時点で、Delphiは動的配列をサポートしています。実行時にサイズを変更でき、他の要素に保存したデータを古いサイズで保持します。レコードやその他の配列を含む同種の要素を保持できます。通常の「静的」配列を宣言するのと同じように動的配列を宣言できますが、単純に配列の境界を省略します。

_var
  ArthurArray: array of TForm;
_

静的配列では下限と上限の両方を指定できますが、動的配列の低インデックスは常にゼロです。高インデックスはHigh関数によって与えられ、常に配列の長さより1つ少ない値を返します。動的配列xHigh(x) = Length(x)-1の場合。

グローバル変数には、ローカルプロシージャを含む任意のコードからアクセスできます。

動的配列型のグローバル変数は、empty配列に初期化されます。その長さはゼロになり、その配列で呼び出されるHighは-1になります。その配列のLowは、ゼロを返します。

いつでも、動的配列のサイズを変更できます。文字列でできるように、SetLength関数を使用します。

_var
  NumElements: Integer;
begin
  NumElements := GetNumberOfArthurForms();
  SetLength(ArthurArray, NumElements);
end;
_

多次元配列がある場合、ループで長さを設定できます。

_var
  matrix: array of array of Double;
  i: Integer;
begin
  SetLength(matrix, height);
  for i := 0 to height - 1 do
    SetLength(matrix[i], width);
end;
_

すべての内部配列の長さを一度に設定するためのショートカットがあります:

_begin
  SetLength(matrix, height, width);
end;
_

前述のように、動的配列はサイズを変更しても古い値を保持します。

_var
  data: array of string;
begin
  SetLength(data, 2);
  data[1] := 'foo';
  SetLength(data, 20);
  Assert(data[1] = 'foo');
end;
_

ただし、配列をshortenすると、新しい最後の要素を超えて存在していた要素は永久に失われます。

_begin
  SetLength(data, 20);
  data[15] := 'foo';
  SetLength(data, 2);
  // data[15] does not exist anymore.
  SetLength(data, 16);
  writeln(data[15); // Should print an *empty* line.
end;
_

上記の私のデモでは文字列を使用しました。文字列はDelphiでは特別です。それらは参照カウントを通じてコン​​パイラーによって管理されます。そのため、string型の新しい動的配列要素は空に初期化されます。しかし、代わりに整数を使用した場合、新しい要素の値の保証はありません。ゼロの場合もありますが、スタンドアロンのローカル変数の初期値のように、それらは他のものでもあります。

Delphi 7のヘルプファイルは非常に優れていると言われています。動的配列の詳細についてはこちらをご覧ください。 Delphiインストールで提供されるVCLおよびRTLソースコード全体、および過去10年間に作成されたほぼすべてのDelphiコード例で、それらの使用のデモを見つけることができます。

90
Rob Kennedy

まず、質問の最初の部分に対する一般的な回答を次に示します。

配列が静的でなくなった場合は、TList、TStringList、またはContnrsユニットの多くのコンテナクラスのいずれかを使用することを検討できます。

彼らはあなたが何をしているのかをより良く表現し、あなたが必要とするかもしれない追加の機能を提供します。並べ替えまたは名前/値のペアは、必要に応じて動的に成長し、非常に最適化されています。


あなたは言った:

「コンテキストは、ファイル内のバイトの線形変換で配列を埋めています。誰かがそれを開きたいと思うまで、ファイルの大きさがわからず、ファイルのサイズは任意です。」

あなたの特定の問題のために、私はファイルを使用してバイトをロードします:

  MyFileStream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
  Size := MyFileStream.Size - MyFileStream.Position;
  SetLength(Buffer, Size);
  MyFileStream.Read(Buffer[0], Size);

次に、PCharポインターを使用して、バッファ内の各文字または各バイトを1つずつ通過し、必要に応じて変換します。

1
lkessler