Delphi Programming / Object Pascal Delphi 一般・その他  
           
        
        
        
          
            
               数値型の配列データのファイルへの保存と読み出し 
             
            
               動作確認等  
               Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32 
             
           
        
        
        
          
            
               整数型の配列データや実数型の配列データも,単一の数値と同じようにファイルに保存して,保存したファイルから読み出すことができます. 
                 当然ですが,保存したファイル,あるいはメモリストリームに格納したデータは,単なるバイトデータです.そのデータが配列のデータであるという情報はありません. 
 
           
        
        
          
            
              01_ 1 次元の静的配列データのファイルへの保存と読み出し 
           
        
        
          
            
               1 次元の静的配列データのファイルへの保存と読み出しの例です.
 
           
        
        
          
            
              図1  
           
        
        
          
            
              リスト1  
           
        
copy code 
        
          implementation {$R *.dfm} const FColCount = 5 ; 
type TDoubleArray = array  [0 ..FColCount - 1 ] of  Double; 
// =============================================================================//   1 次元の静的配列データをファイルへ保存// =============================================================================procedure  TForm1.Button1Click(Sender: TObject);var LSrcDblArray : TDoubleArray; 
  LCol         : Integer; 
  LMemStream   : TMemoryStream; 
begin //  テスト用のデータを配列要素に代入for  LCol := 0  to  Length(LSrcDblArray) - 1  do  begin LSrcDblArray[LCol] := 100  + (LCol + 1 ) / 10.0 ; 
  end ;LMemStream := TMemoryStream.Create; 
  try //  メモリストリームに配列データを格納LMemStream.Size := SizeOf(LSrcDblArray); 
    LMemStream.Write (@LSrcDblArray, LMemStream.Size); 
   //  そのメモリストリームをファイルに保存LMemStream.SaveToFile('1dim-StaticArray.bin' ); 
  finally FreeAndNil(LMemStream); 
  end ;end ;// =============================================================================//   1 次元の静的配列データをファイルから読み出す// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LMemStream   : TMemoryStream; 
  LDstDblArray : TDoubleArray; 
  LCol         : Integer; 
  LResText     : string ; 
begin Memo1.Lines.Clear; 
  LMemStream := TMemoryStream.Create; 
  try //  ファイルのデータをメモリストリームに読み込むLMemStream.LoadFromFile('1dim-StaticArray.bin' ); 
    //  メモリストリームのデータを配列要素に読み込むLMemStream.Position := 0 ; 
    LMemStream.Read (LDstDblArray, LMemStream.Size); 
    //  取得したデータを表示LResText := '' ; 
    for  LCol := 0  to  FColCount - 1  do  begin LResText := LResText + FormatFloat('   000.0' , LDstDblArray[LCol]); 
    end ;Memo1.Lines.Add(LResText); 
  finally FreeAndNil(LMemStream); 
  end ;end ; 
        
          
            
              02_ 2 次元の静的配列データのファイルへの保存と読み出し 
           
        
        
          
            
               2 次元の静的配列データのファイルへの保存と読み出しの例です. 
 
           
        
        
          
            
              図2  
           
        
        
          
            
              リスト2  
           
        
copy code 
        
          implementation {$R *.dfm} const FRowCount = 2 ; 
  FColCount = 5 ; 
type TDoubleArray = array  [0 ..FRowCount - 1 , 0 ..FColCount - 1 ] of  Double; 
// =============================================================================//   2 次元の静的配列データをファイルへ保存// =============================================================================procedure  TForm1.Button1Click(Sender: TObject);var LSrcDblArray : TDoubleArray; 
  LRow         : Integer; 
  LCol         : Integer; 
  LMemStream   : TMemoryStream; 
begin //  テスト用のデータを配列要素に代入for  LRow := 0  to  FRowCount - 1  do  begin for  LCol := 0  to  FColCount - 1  do  begin LSrcDblArray[LRow][LCol] := (LRow + 1 ) * 100  + (LCol + 1 ) / 10.0 ; 
    end ;end ;LMemStream := TMemoryStream.Create; 
  try //  メモリストリームに配列データを格納LMemStream.Size := SizeOf(LSrcDblArray); 
    LMemStream.Write (@LSrcDblArray, LMemStream.Size); 
   //  そのメモリストリームをファイルに保存LMemStream.SaveToFile('2dim-StaticArray.bin' ); 
  finally FreeAndNil(LMemStream); 
  end ;end ;// =============================================================================//   2 次元の静的配列データをファイルから読み出す// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LMemStream   : TMemoryStream; 
  LDstDblArray : TDoubleArray; 
  LRow         : Integer; 
  LCol         : Integer; 
  LResText     : string ; 
begin Memo1.Lines.Clear; 
  LMemStream := TMemoryStream.Create; 
  try //  ファイルのデータをメモリストリームに読み込むLMemStream.LoadFromFile('2dim-StaticArray.bin' ); 
    //  メモリストリームのデータを配列要素に読み込むLMemStream.Position := 0 ; 
    LMemStream.Read (LDstDblArray, LMemStream.Size); 
    //  取得したデータを表示for  LRow := 0  to  FRowCount - 1  do  begin LResText := '' ; 
      for  LCol := 0  to  FColCount - 1  do  begin LResText := LResText + FormatFloat('   000.0' , LDstDblArray[LRow][LCol]); 
      end ;Memo1.Lines.Add(LResText); 
    end ;finally FreeAndNil(LMemStream); 
  end ;end ; 
        
          
            
              03_ 1 次元の動的配列データのファイルへの保存と読み出し 
           
        
        
          
            
               1 次元の動的配列データのファイルへの保存と読み出しの例です. 
                 ディスク上のファイルから TMemoryStream にデータを読み込むと,それは配列要素全体のバイト数です.それを要素の変数の型のサイズで割ると配列の要素数になります.この値を使用して動的配列の要素数を設定します. 
 
           
        
        
          
            
              図3  
           
        
        
          
            
              リスト3  
           
        
copy code 
        
          implementation uses System.Types; 
{$R *.dfm} // =============================================================================//   1 次元の動的配列データをファイルへ保存//   TDoubleDynArray の使用には uses に System.Types が必要// =============================================================================procedure  TForm1.Button1Click(Sender: TObject);const LColCount : Integer = 5 ; 
var LSrcDblArray : TDoubleDynArray; 
  LCol         : Integer; 
  LMemStream   : TMemoryStream; 
begin SetLength(LSrcDblArray, LColCount); 
  //  テスト用のデータを配列要素に代入for  LCol := 0  to  LColCount - 1  do  begin LSrcDblArray[LCol] := 100  + (LCol + 1 ) / 10.0 ; 
  end ;LMemStream := TMemoryStream.Create; 
  try //  メモリストリームに配列データを格納//  動的配列はポインタなので最初の要素のアドレスを指定するLMemStream.Size := LColCount * SizeOf(Double); 
    LMemStream.Write (@LSrcDblArray[0 ], LMemStream.Size); 
   //  そのメモリストリームをファイルに保存LMemStream.SaveToFile('1dim-DynArray.bin' ); 
  finally FreeAndNil(LMemStream); 
  end ;end ;// =============================================================================//   1 次元の動的配列データをファイルから読み出す//   TDoubleDynArray の使用には uses に System.Types が必要// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LMemStream   : TMemoryStream; 
  LDstDblArray : TDoubleDynArray; 
  LColCount    : Integer; 
  LCol         : Integer; 
  LResText     : string ; 
begin Memo1.Lines.Clear; 
  LMemStream := TMemoryStream.Create; 
  try //  ファイルのデータをメモリストリームに読み込むLMemStream.LoadFromFile('1dim-DynArray.bin' ); 
    //  配列の要素数を計算LColCount := LMemStream.Size div  SizeOf(Double); 
    //  配列の要素数を設定 (配列用のメモリの確保)SetLength(LDstDblArray, LColCount); 
    //  メモリストリームのデータを配列要素に読み込む//  動的配列はポインタなので最初の要素の値を指定するLMemStream.Position := 0 ; 
    LMemStream.Read (LDstDblArray[0 ], LMemStream.Size); 
    //  取得したデータを表示LResText := '' ; 
    for  LCol := 0  to  LColCount - 1  do  begin LResText := LResText + FormatFloat('   000.0' , LDstDblArray[LCol]); 
    end ;Memo1.Lines.Add(LResText); 
  finally FreeAndNil(LMemStream); 
  end ;end ; 
        
          
            
              04_ 2 次元の動的配列データのファイルへの保存と読み出し 
           
        
        
          
            
               2 次元の動的配列データのファイルへの保存と読み出しの例です.
                 2 次元の動的配列は,その各行が 1 次元配列のポインタとなっています.そのため,TMemoryStream に配列要素の値を格納する時は行単位で行います.各行の最初の要素のデータのアドレスを指定して,1 行分の配列テータを TMemoryStream に格納していきます. 
 
           
        
        
          
            
              図4  
           
        
        
          
            
              リスト4  
           
        
copy code 
        
          implementation {$R *.dfm} type TDouble2DDynArray = array  of  array  of  Double; 
// =============================================================================//   2 次元の動的配列データをファイルへ保存// =============================================================================procedure  TForm1.Button1Click(Sender: TObject);const LRowCount : Integer = 3 ; 
  LColCount : Integer = 5 ; 
var LSrcDblArray : TDouble2DDynArray; 
  LRow         : Integer; 
  LCol         : Integer; 
  LMemStream   : TMemoryStream; 
begin //  配列の要素数を設定して,テスト用のデータを配列要素に代入SetLength(LSrcDblArray, LRowCount, LColCount); 
  for  LRow := 0  to  LRowCount - 1  do  begin for  LCol := 0  to  LColCount - 1  do  begin LSrcDblArray[LRow][LCol] := (LRow + 1 ) * 100  + (LCol + 1 ) / 10.0 ; 
    end ;end ;LMemStream := TMemoryStream.Create; 
  try //  メモリストリームに行数と列数の値を格納LMemStream.WriteData(LRowCount); 
    LMemStream.WriteData(LColCount); 
    //  メモリストリームに配列データを格納//  動的配列の各行はポインタなので各行の最初の要素のアドレスを指定するfor  LRow := 0  to  LRowCount - 1  do  begin LMemStream.Write (@LSrcDblArray[LRow][0 ], LColCount * SizeOf(Double)); 
    end ;//  そのメモリストリームをファイルに保存LMemStream.SaveToFile('2dim-DynArray.bin' ); 
  finally FreeAndNil(LMemStream); 
  end ;end ;// =============================================================================//   2 次元の動的配列データをファイルから読み出す// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LMemStream   : TMemoryStream; 
  LDstDblArray : TDouble2DDynArray; 
  LRowCount    : Integer; 
  LColCount    : Integer; 
  LRow         : Integer; 
  LCol         : Integer; 
  LResText     : string ; 
begin Memo1.Lines.Clear; 
  LMemStream := TMemoryStream.Create; 
  try //  ファイルのデータをメモリストリームに読み込むLMemStream.LoadFromFile('2dim-DynArray.bin' ); 
    //  配列の行数と列数の値をメモリストリームから読み出すLMemStream.Position := 0 ; 
    LMemStream.ReadData(LRowCount); 
    LMemStream.ReadData(LColCount); 
    //  配列の行数と列数を設定 (配列用のメモリの確保)SetLength(LDstDblArray, LRowCount, LColCount); 
    //  メモリストリームのデータを配列要素に読み込む//  動的配列の各行はポインタなので,各行の先頭要素を指定して行単位で読み込むfor  LRow := 0  to  LRowCount - 1  do  begin LMemStream.Read (LDstDblArray[LRow][0 ], LColCount * SizeOf(Double)); 
    end ;//  取得したデータを表示for  LRow := 0  to  LRowCount - 1  do  begin LResText := '' ; 
      for  LCol := 0  to  LColCount - 1  do  begin LResText := LResText + FormatFloat('   000.0' , LDstDblArray[LRow][LCol]); 
      end ;Memo1.Lines.Add(LResText); 
    end ;finally FreeAndNil(LMemStream); 
  end ;end ; 
        
          
            
              05_ 2 GB を超える配列データのファイルへの保存と読み出し 
           
        
        
          
            
               ここまでのサンプルでは,保存と読み出しに TMemorySteam を使用しています.TMemoryStream は,32 ビット,64 ビットに関係なく,2 GB を超えるデータは扱えません.2 GB 以上のデータを扱うには,例えば,TFileStream を使用することができます.その例です.
 
           
        
        
          
            
              図5 
                  EXE は 64 ビット 
                  配列の要素は 24575 x 24575 
                  配列データのサイズ約 4.8 GB 
                  [メモリ確保] [保存] [読み込み] の順に実行 
                  保存に要した時間は 10.3 秒 
                  
           
        
        
          
            
              リスト5  
           
        
copy code 
        
          unit  Unit1;interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls; 
type TForm1 = class (TForm) 
    Button1: TButton; 
    Memo1: TMemo; 
    Button2: TButton; 
    Button3: TButton; 
    procedure  FormCreate(Sender: TObject);procedure  Button1Click(Sender: TObject);procedure  Button2Click(Sender: TObject);procedure  Button3Click(Sender: TObject);private { Private 宣言 } public { Public 宣言 } end ;var Form1: TForm1; 
implementation uses Winapi.MMSystem; 
{$R *.dfm} type T2DDynArray = array  of  array  of  Double; 
const FSize : NativeInt = $5FFF ; 
var FArray1   : T2DDynArray; 
  FFilePath : string ; 
// =============================================================================//   フォーム生成時の処理// =============================================================================procedure  TForm1.FormCreate(Sender: TObject);begin FFilePath := ExtractFilePath(Application.ExeName) + '000.dat' ; 
end ;// =============================================================================//   動的配列のメモリ確保//   2 GB を超える 2 次元の動的配列の例// =============================================================================procedure  TForm1.Button1Click(Sender: TObject);var LTime  : Cardinal; 
  LBytes : UInt64; 
  I      : NativeInt; 
  J      : NativeInt; 
begin Screen.Cursor := crHourGlass; 
  Memo1.Lines.Clear; 
  Memo1.Update; 
  LTime := timeGetTime; 
  LBytes := FSize * FSize * SizeOf(Double); 
  Memo1.Lines.Add(FormatFloat(',0' , LBytes) + ' Byte' ); 
  SetLength(FArray1, FSize, FSize); 
  //   動的配列の要素に数値を代入for  I := 0  to  Length(FArray1) - 1  do  begin for  J := 0  to  Length(FArray1[I]) - 1  do  begin FArray1[I][J] := 1.0 ; 
    end ;end ;FArray1[0 ][0 ] := 123.456 ; 
  FArray1[FSize - 1 ][FSize - 1 ] := 654.123 ; 
  Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End' ); 
  Screen.Cursor := crDefault; 
end ;// =============================================================================//   2 次元の動的配列のデータをファイルに保存//   TFileStream を使用する例// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LTime       : Cardinal; 
  I           : NativeInt; 
  LFileStream : TFileStream; 
begin if  Length(FArray1) = 0  then  begin Exit; 
  end ;Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
 //   ファイルに保存LFileStream := TFileStream.Create(FFilePath, fmCreate); 
  try for  I := 0  to  Length(FArray1) - 1  do  begin LFileStream.Write (FArray1[I][0 ], Length(FArray1[I]) * SizeOf(Double)); 
    end ;finally FreeAndNil(LFileStream); 
  end ;Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- SaveData' ); 
  Screen.Cursor := crDefault; 
end ;// =============================================================================//   2 次元の動的配列のデータをファイルから読み込む//   TFileStream を使用する例// =============================================================================procedure  TForm1.Button3Click(Sender: TObject);var LTime       : Cardinal; 
  I           : NativeInt; 
  LFileStream : TFileStream; 
  Lvalue  : Double; 
begin if  not  FileExists(FFilePath) then  begin Exit; 
  end ;if  Length(FArray1) = 0  then  begin Exit; 
  end ;FArray1[0 ][0 ] := 0 ; 
  FArray1[FSize - 1 ][FSize - 1 ] := 0 ; 
  Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
  //   ファイルから読み込むLFileStream := TFileStream.Create(FFilepath, fmOpenRead); 
  try for  I := 0  to  Length(FArray1) - 1  do  begin LFileStream.Read (FArray1[I][0 ], Length(FArray1[I]) * SizeOf(Double)); 
    end ;finally FreeAndNil(LFileStream); 
  end ;//  確認のために特定要素の値を表示Lvalue := FArray1[0 ][0 ]; 
  Memo1.Lines.Add(FormatFloat(',0.000' , Lvalue)); 
  Lvalue := FArray1[FSize - 1 ][FSize - 1 ]; 
  Memo1.Lines.Add(FormatFloat(',0.000' , Lvalue)); 
  Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- ReadData' ); 
  Screen.Cursor := crDefault; 
end ;end . 
        
          
            
               上のサンプルは,行単位で処理しています.以下は,配列の要素単位で処理した場合です.
 
           
        
        
          
            
              図6 
                  EXE は 64 ビット 
                  配列の要数素は 24575 x 24575 
                  配列データのサイズ約 4.8 GB 
                  [メモリ確保] [保存] [読み込み] の順に実行 
                  保存に要した時間は 805.9 秒 (13 分 25.9 秒) 
                  
           
        
        
          
            
              リスト6  
           
        
copy code 
        
          // =============================================================================//   2 次元の動的配列のデータをファイルに保存//   要素単位で保存する場合// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LTime       : Cardinal; 
  I           : NativeInt; 
  J           : NativeInt; 
  LFileStream : TFileStream; 
begin if  Length(FArray1) = 0  then  begin Exit; 
  end ;Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
 //   ファイルに保存LFileStream := TFileStream.Create(FFilePath, fmCreate); 
  try for  I := 0  to  Length(FArray1) - 1  do  begin for  J := 0  to  Length(FArray1[I]) - 1  do  begin LFileStream.Write (FArray1[I][J], SizeOf(Double)); 
      end ;end ;finally FreeAndNil(LFileStream); 
  end ;Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- SaveData' ); 
  Screen.Cursor := crDefault; 
end ;// =============================================================================//   2 次元の動的配列のデータをファイルから読み込む//   要素単位で読み込む場合// =============================================================================procedure  TForm1.Button3Click(Sender: TObject);var LTime       : Cardinal; 
  I           : NativeInt; 
  J           : NativeInt; 
  LFileStream : TFileStream; 
  Lvalue  : Double; 
begin if  not  FileExists(FFilePath) then  begin Exit; 
  end ;if  Length(FArray1) = 0  then  begin Exit; 
  end ;FArray1[0 ][0 ] := 0 ; 
  FArray1[FSize - 1 ][FSize - 1 ] := 0 ; 
  Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
  //   ファイルから読み込むLFileStream := TFileStream.Create(FFilepath, fmOpenRead); 
  try for  I := 0  to  Length(FArray1) - 1  do  begin for  J := 0  to  Length(FArray1[I]) - 1  do  begin LFileStream.Read (FArray1[I][J], SizeOf(Double)); 
      end ;end ;finally FreeAndNil(LFileStream); 
  end ;//  確認のために特定要素の値を表示Lvalue := FArray1[0 ][0 ]; 
  Memo1.Lines.Add(FormatFloat(',0.000' , Lvalue)); 
  Lvalue := FArray1[FSize - 1 ][FSize - 1 ]; 
  Memo1.Lines.Add(FormatFloat(',0.000' , Lvalue)); 
  Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- ReadData' ); 
  Screen.Cursor := crDefault; 
end ; 
        
          
            
               下図は,配列の要素数を 4095 x 4095 ($FFF x $FFF) にして,テータのサイズを約 32 分の 1 にした結果です.それでも保存に 22.4 秒かかっています.
 
           
        
        
          
            
              図7 
                  配列の要素数は 4095 x 4095 ($FFF x $FFF) 
                  配列データのサイズ約 134 MB 
                  [メモリ確保] [保存] [読み込み] の順に実行 
                  保存に要した時間は 22.4 秒 
                  
           
        
        
          
            
               本項の最初のサンプルと同じように,行単位で処理すると下図の結果になります.保存に要した時間が 0.094 秒と,大幅に短くなりました.
                仮想メモリを多く使用するようなサイズの配列の場合は,行単位で処理しても処理時間の短縮は期待できません.仮想メモリのドライブが SSD であれば,処理時間の短縮が期待できるかも知れません.
 
           
        
        
          
            
              図8 
                  配列の要素数は 4095 x 4095 ($FFF x $FFF) 
                  配列データのサイズ約 134 MB 
                  行単位で TMemoryStream を使用した場合 
                  保存に要した時間は 0.094 秒 
                  
           
        
        
          
            
              06_ ファイル型 File of Double による配列データの保存と読み出し 
           
        
        
          
            
              ファイル型を使用してファイルにアクセスすれば,2 GB を超える動的配列のデータが扱えます.
 
           
        
        
          
            
              図9 
                  EXE は 64 ビット 
                  配列の要素は 24575 x 24575 
                  配列データのサイズ約 4.8 GB 
                  [メモリ確保] [保存] [読み込み] の順に実行 
                  
           
        
        
          
            
              リスト7  
           
        
copy code 
        
          unit  Unit1;interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls; 
type TForm1 = class (TForm) 
    Button1: TButton; 
    Memo1: TMemo; 
    Button2: TButton; 
    Button3: TButton; 
    procedure  FormCreate(Sender: TObject);procedure  Button1Click(Sender: TObject);procedure  Button2Click(Sender: TObject);procedure  Button3Click(Sender: TObject);private { Private 宣言 } public { Public 宣言 } end ;var Form1: TForm1; 
implementation uses Winapi.MMSystem; 
{$R *.dfm} type T2DDynArray = array  of  array  of  Double; 
const FSize : NativeInt = $5FFF ; 
var FArray1   : T2DDynArray; 
  FFilePath : string ; 
// =============================================================================//   フォーム生成時の処理// =============================================================================procedure  TForm1.FormCreate(Sender: TObject);begin FFilePath := ExtractFilePath(Application.ExeName) + '000.dat' ; 
end ;// =============================================================================//   動的配列のメモリ確保//   2 GB を超える 2 次元の動的配列の例// =============================================================================procedure  TForm1.Button1Click(Sender: TObject);var LTime  : Cardinal; 
  LBytes : UInt64; 
  I      : NativeInt; 
  J      : NativeInt; 
begin Screen.Cursor := crHourGlass; 
  Memo1.Lines.Clear; 
  Memo1.Update; 
  LTime := timeGetTime; 
  LBytes := FSize * FSize * SizeOf(Double); 
  Memo1.Lines.Add(FormatFloat(',0' , LBytes) + ' Byte' ); 
  SetLength(FArray1, FSize, FSize); 
  //   動的配列の要素に数値を代入for  I := 0  to  Length(FArray1) - 1  do  begin for  J := 0  to  Length(FArray1[I]) - 1  do  begin FArray1[I][J] := 1.0 ; 
    end ;end ;FArray1[0 ][0 ] := 123.456 ; 
  FArray1[FSize - 1 ][FSize - 1 ] := 654.123 ; 
  Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End' ); 
  Screen.Cursor := crDefault; 
end ;// =============================================================================//   2 次元の動的配列のデータをファイルに保存//   ファイル型 File of Double を使用する例// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LTime : Cardinal; 
  I     : NativeInt; 
  LFile : File  of  Double; 
begin if  Length(FArray1) = 0  then  begin Exit; 
  end ;Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
  //  保存ファイル名をファイル型の変数に割り当てるAssignFile(LFile, FFilePath); 
  try //  新規にファイルを作成して開く(既存ファイルは上書き)Rewrite(LFile); 
    for  I := 0  to  Length(FArray1) - 1  do  begin //  データをファイルに書き込むBlockWrite(LFile, FArray1[I][0 ], Length(FArray1[I])); 
    end ;finally //  ファイルを閉じるCloseFile(LFile); 
  end ;Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- SaveData' ); 
  Screen.Cursor := crDefault; 
end ;// =============================================================================//   2 次元の動的配列のデータをファイルから読み込む//   ファイル型 File of Double を使用する例// =============================================================================procedure  TForm1.Button3Click(Sender: TObject);var LTime  : Cardinal; 
  I      : NativeInt; 
  LFile  : File  of  Double; 
  Lvalue : Double; 
begin if  not  FileExists(FFilePath) then  begin Exit; 
  end ;if  Length(FArray1) = 0  then  begin Exit; 
  end ;FArray1[0 ][0 ] := 0 ; 
  FArray1[FSize - 1 ][FSize - 1 ] := 0 ; 
  Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
  //  保存ファイル名をファイル型の変数に割り当てるAssignFile(LFile, FFilePath); 
  try //  ファイルを開くReset(LFile); 
    //  ファイルからデータを読み込むfor  I := 0  to  Length(FArray1) - 1  do  begin BlockRead(LFile, FArray1[I][0 ], Length(FArray1[I])); 
    end ;finally //  ファイルを閉じるCloseFile(LFile); 
  end ;//  確認のために特定要素の値を表示Lvalue := FArray1[0 ][0 ]; 
  Memo1.Lines.Add(FormatFloat(',0.000' , Lvalue)); 
  Lvalue := FArray1[FSize - 1 ][FSize - 1 ]; 
  Memo1.Lines.Add(FormatFloat(',0.000' , Lvalue)); 
  Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- ReadData' ); 
  Screen.Cursor := crDefault; 
end ;end . 
        
          
            
              07_ FileWrite / FileRead 関数で配列データの保存と読み出し 
           
        
        
          
            
               FileWrite 関数や FileRead 関数を使用してファイルにアクセスすれば,2 GB 以上の動的配列データを扱えます.そのサンプルです.これらの関数類は,内部で Windows API の関数を使用して処理しています.
 
           
        
        
          
            
              図10 
                  EXE は 64 ビット 
                  配列の要素は 24575 x 24575 
                  配列データのサイズ約 4.8 GB 
                  [メモリ確保] [保存] [読み込み] の順に実行 
                  
           
        
        
          
            
              リスト8  
           
        
copy code 
        
          unit  Unit1;interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, 
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls; 
type TForm1 = class (TForm) 
    Button1: TButton; 
    Memo1: TMemo; 
    Button2: TButton; 
    Button3: TButton; 
    procedure  FormCreate(Sender: TObject);procedure  Button1Click(Sender: TObject);procedure  Button2Click(Sender: TObject);procedure  Button3Click(Sender: TObject);private { Private 宣言 } public { Public 宣言 } end ;var Form1: TForm1; 
implementation uses Winapi.MMSystem; 
{$R *.dfm} type T2DDynArray = array  of  array  of  Double; 
const FSize : NativeInt = $5FFF ; 
var FArray1   : T2DDynArray; 
  FFilePath : string ; 
// =============================================================================//   フォーム生成時の処理// =============================================================================procedure  TForm1.FormCreate(Sender: TObject);begin FFilePath := ExtractFilePath(Application.ExeName) + '000.dat' ; 
end ;// =============================================================================//   動的配列のメモリ確保//   2 GB を超える 2 次元の動的配列の例// =============================================================================procedure  TForm1.Button1Click(Sender: TObject);var LTime  : Cardinal; 
  LBytes : UInt64; 
  I      : NativeInt; 
  J      : NativeInt; 
begin Screen.Cursor := crHourGlass; 
  Memo1.Lines.Clear; 
  Memo1.Update; 
  LTime := timeGetTime; 
  LBytes := FSize * FSize * SizeOf(Double); 
  Memo1.Lines.Add(FormatFloat(',0' , LBytes) + ' Byte' ); 
  SetLength(FArray1, FSize, FSize); 
  //  動的配列の要素に数値を代入for  I := 0  to  Length(FArray1) - 1  do  begin for  J := 0  to  Length(FArray1[I]) - 1  do  begin FArray1[I][J] := 1.0 ; 
    end ;end ;FArray1[0 ][0 ] := 123.456 ; 
  FArray1[FSize - 1 ][FSize - 1 ] := 654.123 ; 
  Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End' ); 
  Screen.Cursor := crDefault; 
end ;// =============================================================================//   2 次元の動的配列のデータをファイルに保存//   FileCreate. FileWrite を使用する例// //   FileCreate は Windows API の CreateFile 関数でファイルを作成する//   FileWrite は Windows API の WriteFile 関数を使用している// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LTime    : Cardinal; 
  I        : NativeInt; 
  LFHandle : THandle; 
begin if  Length(FArray1) = 0  then  begin Exit; 
  end ;Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
  //  保存ファイル名のファイルを作成LFHandle := FileCreate(FFilePath); 
  try for  I := 0  to  Length(FArray1) - 1  do  begin //  データをファイルに書き込むFileWrite(LFHandle, FArray1[I][0 ], Length(FArray1[I]) * SizeOf(Double)); 
    end ;finally //  ファイルを閉じるFileClose(LFHandle); 
  end ;Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- SaveData' ); 
  Screen.Cursor := crDefault; 
end ;// =============================================================================//   2 次元の動的配列のデータをファイルから読み込む//   FileOpen, FileRead を使用する例// //   FileOpen は Windows API の OpenFile 関数を使用している//   FileRead は Windows API の ReadFile 関数を使用している// =============================================================================procedure  TForm1.Button3Click(Sender: TObject);var LTime    : Cardinal; 
  I        : NativeInt; 
  LFHandle : THandle; 
begin if  not  FileExists(FFilePath) then  begin Exit; 
  end ;if  Length(FArray1) = 0  then  begin Exit; 
  end ;FArray1[0 ][0 ] := 0 ; 
  FArray1[FSize - 1 ][FSize - 1 ] := 0 ; 
  Memo1.Lines.Add('' ); 
  Screen.Cursor := crHourGlass; 
  LTime := timeGetTime; 
  //  ファイルを開くLFHandle := FileOpen(FFilePath, fmOpenRead); 
  try //  ファイルからデータを読み込むfor  I := 0  to  Length(FArray1) - 1  do  begin FileRead(LFHandle, FArray1[I][0 ], Length(FArray1[I]) * SizeOf(Double)); 
    end ;finally //  ファイルを閉じるFileClose(LFHandle); 
  end ;Memo1.Lines.Add(FormatFloat('#0.000 sec' , (timeGetTime - LTime)  / 1000 )); 
  Memo1.Lines.Add('End -- ReadData' ); 
  Screen.Cursor := crDefault; 
end ;end . 
        
          
            
              08_ 3 次元の配列データのファイルへの保存と読み出し 
           
        
        
          
            
               3 次元の動的配列のデータは,2 次元の配列データのサンプルと同じ方法でファイルに保存したり,ファイルから読み込めます.
 
           
        
        
          
            
              図11 
                  EXE は 64 ビット 
                  配列の要素数は 256 x 356 x 256 
                  配列データのサイズ約 134 MB 
                  [メモリ確保] [保存] [読み込み] の順に実行 
                  保存に要した時間は 0.168 秒 
                  
           
        
        
          
            
               要素単位で処理すると,どのぐらい時間がかかるかを調べてみました.保存に要した時間は,上の図では 0.168 秒になっていて,1 秒未満ですが,要素単位で処理すると 22.9 秒となりました.
 
           
        
        
          
            
              図12 
                  配列の要素単位で処理した場合 
                  保存に要した時間は 22.9 秒 
                  
           
        
        
          
            
              リスト9  
           
        
copy code 
        
          // =============================================================================//   3 次元の動的配列のデータをファイルに保存// //   FArray1   : 3 次元の動的配列 (要素のデータは Double 型と仮定)//   FFilePath : 保存先のファイル名// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var I        : NativeInt; 
  J        : NativeInt; 
  LFHandle : THandle; 
begin //  保存ファイル名のファイルを作成LFHandle := FileCreate(FFilePath); 
  try for  I := 0  to  Length(FArray1) - 1  do  begin for  J := 0  to  Length(FArray1[I]) - 1  do  begin //  データをファイルに書き込むFileWrite(LFHandle, FArray1[I][J][0 ], Length(FArray1[I][J]) * SizeOf(Double)); 
      end ;end ;finally //  ファイルを閉じるFileClose(LFHandle); 
  end ;end ;// =============================================================================//   3 次元の動的配列のデータをファイルから読み込む// //   FArray1   : 3 次元の動的配列 (要素のデータは Double 型と仮定)//   FFilePath : 保存先のファイル名// =============================================================================procedure  TForm1.Button3Click(Sender: TObject);var I        : NativeInt; 
  J        : NativeInt; 
  LFHandle : THandle; 
begin //  ファイルを開くLFHandle := FileOpen(FFilePath, fmOpenRead); 
  try //  ファイルからデータを読み込むfor  I := 0  to  Length(FArray1) - 1  do  begin for  J := 0  to  Length(FArray1[I]) - 1  do  begin FileRead(LFHandle, FArray1[I][J][0 ], Length(FArray1[I][J]) * SizeOf(Double)); 
      end ;end ;finally //  ファイルを閉じるFileClose(LFHandle); 
  end ;end ; 
        
          
            
               静的配列のデータはメモリ上で連続しています.したがって,次のようなコードで処理できます.
                 静的配列の場合,SizeOf(FArray1) の値は,配列 FArray1 のデータの全バイト数です.
 
           
        
        
          
            
              図13 
                  配列の要数素は 256 x 256 x 256 
                  配列データのサイズ約 134 MB 
                  [メモリ確保] [保存] [読み込み] の順に実行 
                  
           
        
        
          
            
              リスト10  
           
        
copy code 
        
          // =============================================================================//   3 次元の静的配列のデータをファイルに保存// //   FArray1   : 3 次元の静的配列//               要素のデータは Double 型と仮定//               全ての次元の最初の要素番号が 0 と仮定//   FFilePath : 保存先のファイル名// =============================================================================procedure  TForm1.Button2Click(Sender: TObject);var LFHandle : THandle; 
begin //  保存ファイル名のファイルを作成LFHandle := FileCreate(FFilePath); 
  try FileWrite(LFHandle, FArray1[0 ][0 ][0 ], SizeOf(FArray1)); 
  finally //  ファイルを閉じるFileClose(LFHandle); 
  end ;end ;// =============================================================================//   3 次元の静的配列のデータをファイルから読み込む// //   FArray1   : 3 次元の静的配列//               要素のデータは Double 型と仮定//               全ての次元の最初の要素番号が 0 と仮定//   FFilePath : 保存先のファイル名// =============================================================================procedure  TForm1.Button3Click(Sender: TObject);var LFHandle : THandle; 
begin //  ファイルを開くLFHandle := FileOpen(FFilePath, fmOpenRead); 
  try //  ファイルからデータを読み込むFileRead(LFHandle, FArray1[0 ][0 ][0 ], SizeOf(FArray1)); 
  finally //  ファイルを閉じるFileClose(LFHandle); 
  end ;end ;