ストリームを配列として使う方法について

解決


Snake  2007-04-18 19:09:26  No: 25833

よろしくお願いします。
TMemoryStream にデータが入っているのですが。
ここには  Double X, doubleY の位置情報があります。

プログラム的に、作成される場所では以下のように作成されています。
for i := 0 to N do {
MemStr.write(DblX, SizeOf(Double))
MemStr.write(DblY, SizeOf(Double))
}

このデータから処理を行うのですが、
MemStrが渡されてきて
このままでは処理しにくいので、
Xp: array [0..N] of Double;
Yp: array [0..N] of Double;
として処理したいのですが
N が不定なのでどうしたものかと悩んでいます。

とりあえず
N := MemStr.Size / 8 / 2;
となるのはわかるのですが、
この後どうやってAllocMemするのかもわかりません。
もっと他にやりやすい方法があればそれもおしえてくれませんか?


AllocMemでなくて...  2007-04-18 20:20:23  No: 25834

◇2個のDouble値を要素とする構造体の動的配列
◇MemoryStream.Memory

中級者なら、このキーワードで理解できるね?


うんと  2007-04-19 07:28:54  No: 25835

えー、動的配列で Length() 使うだけじゃないの。
動的配列は、まさに実行時にサイズを決められることが利点なわけで。


Snake  2007-04-26 20:39:34  No: 25836

えっと、
MemoryStreamには  Double の線分データ(x1, y1, x2, y2)として
データが4個ずつ連続して入っています。
これを  TList で線分ごとにリスト化しようとしています。
現在以下のようになっているのですが、未だに動作しません。
どうすればよいのでしょうか?

var
  PNum: array of Double;
begin
  PNum := AllocMem(SizeOf(Double) * 4);
  PNum[0] := X1;
  PNum[1] := Y1;
  PNum[2] := X1;
  PNum[3] := Y1;
  List.Add(PNum);
end;

消去する場合は
var
  i:      Integer;
  PNum:   array of Double;
begin
  for i := 1 to Count do begin
    PNum := List.Items[i];
    FreeMem(PNum);
  end;
end;


ナンでList?  2007-04-26 21:19:16  No: 25837

動的配列のメモリ割り当ては SetLengthで出来るんだから、
わざわざTListやAllocMemを持ち出す必要はないんじゃないの?


うんと  2007-04-26 22:18:44  No: 25838

質問の内容が最初と微妙に変化してますね。

このような場合は、レコード型かクラスのインスタンスのリストを作るのが常道です。
クラスの方が好ましいのですが、ここでは質問内容に近いレコード型のリストをつくる例を示します。

unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

// データの型とそのポインタの定義
type
  PSenbunData = ^TSenbunData;
  TSenbunData = record
    X1,Y1,X2,Y2: double;
  end;

var
  ms: TMemoryStream;
  lt: TList;

// メモリストリームとリストのインスタンスを作成
procedure TForm1.FormCreate(Sender: TObject);
begin
  ms := TMemoryStream.Create;
  lt := TList.Create;
end;

// メモリストリームとリストのインスタンスを破棄
procedure TForm1.FormDestroy(Sender: TObject);
var
  i: integer;
begin
  ms.Free;

  for i := lt.Count - 1 downto 0 do
    Dispose(PSenbunData(lt[i]));  //項目は個別に破棄

  lt.Free;
end;

// 2つのデータをメモリストリームに入れる
procedure TForm1.Button1Click(Sender: TObject);
var
  x1,y1,x2,y2,x3,y3,x4,y4: Double;
begin
  x1 := 1.2; y1 := 2.3;
  x2 := 3.4; y2 := 4.5;
  x3 := 5.6; y3 := 6.7;
  x4 := 7.8; y4 := 8.9;

  ms.Write(x1, SizeOf(Double));
  ms.Write(y1, SizeOf(Double));
  ms.Write(x2, SizeOf(Double));
  ms.Write(y2, SizeOf(Double));
  ms.Write(x3, SizeOf(Double));
  ms.Write(y3, SizeOf(Double));
  ms.Write(x4, SizeOf(Double));
  ms.Write(y4, SizeOf(Double));
end;

// メモリストリームからデータを読み込んでリストに追加
procedure TForm1.Button2Click(Sender: TObject);
var
  n, i: integer;
  pData: PSenbunData;
begin
  n := ms.Size div SizeOf(TSenbunData);
  ms.Position := 0;
  for i := 0 to n-1 do
  begin
    New(pData);
    ms.Read(pData^, SizeOf(TSenbunData));
    lt.Add(pData);
  end;
end;

// リストの項目にアクセスして表示
procedure TForm1.Button3Click(Sender: TObject);
var
  data: TSenbunData;
  i: integer;
begin
  Memo1.Clear;
  for i := 0 to lt.Count -1 do
  begin
    data := PSenbunData(lt[i])^;
    Memo1.Lines.Add(FloatToStr(data.X1));
    Memo1.Lines.Add(FloatToStr(data.Y1));
    Memo1.Lines.Add(FloatToStr(data.X2));
    Memo1.Lines.Add(FloatToStr(data.Y2));
  end;
end;

end.


Snake  2007-04-26 22:21:19  No: 25839

TList を使うのは
線分の削除や追加をやりやすくするためなのですが

具体的には、
PaintBoxにベクターデータの線を描画します。
この線は連続した閉じた図形のデータです。

また、デフォルトはMemoryStreamにデータが入れられ、
これからポイントの座標を取り込みます。
このデータは数千個になる場合もあります。
そこから編集でポイントを追加したり削除したりして
データを変更していきます。

最初は
TVPoint = class(TPersistent)
public
  X1: double;
  Y1: double;
  X2: double;
  Y2: double;
end;
というクラスを作って、

VPoint = TVPoint.Create;
を行って
List.Add(VPoint);
でデータを作っていたのですが
少ないデータならいいのですが、ポイント数が多くなると
どうもCreateで時間がかかるのか処理が遅いので
Doubleの配列を使ってやろうと思ったのです。

それから最後に、SetLength を使ってどのようにすればいいのか
わからないのですが、教えてください。


Snake  2007-04-26 23:42:03  No: 25840

’うんと’さんどうもありがとうございます。
おかげさまで思い通りのことができました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加