掲示板システム
ホーム
アクセス解析
カテゴリ
ログアウト
可変長データの循環バッファによる書き込みの最適な方法は? (ID:40192)
名前
ホームページ(ブログ、Twitterなど)のURL (省略可)
本文
出遅れましたが、 私も以前データロガーで同じような事を妄想したことが有ります。 せっかくの機会なので書いてみました。 ただTListは循環式のデータを扱うのに適していないので独自クラスで書きました。 原理は ディスク領域に対して データ1......... データ2....................... データ3... データ4.......... データ5............... とデータ追加時に追加分のデータをファイルに書き込み データサイズが規定を超えたら データ6...................... データ3... データ4.......... データ5............... と古いデータのあったファイル領域に新しいデータを書きこみます。 メモリー内ではでは古いデータは消去、 新しい領域に新しいデータを格納します。 つまり、全データ読み込み式、逐次書き込みタイプのデータベースです。 サンプルの意味を込めてなるべく簡潔に書こうと努力しましたが、 読み返してみるとちょっと読みづらいですね。 要約するとTVariableDataが前後のデータと手を繋ぐ形のデータ型で、 1-2-3-4-5 2-3-4-5-6 とデータの前後の追加削除に最適な形式を取っています。 TVariableDataListはそれを包括したクラスで、ファイル出力を備えています。 数キロのファイルでしか実験していませんが、理論上はどこまで大きなファイルサイズでも同じ速度で書き込み出来るはずです。 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls; type TVariableData = class(TObject) private procedure SetNext(const Value: TVariableData); procedure SetPre(const Value: TVariableData); public ID:Integer; //追加順ユニークID Str:string; //可変長データ fPre :TVariableData; fNext:TVariableData; function Size:Integer; procedure WriteFile(aStream:TFileStream); procedure SaveToStream(aStream:TMemoryStream); function LoadFromStream(aStream:TMemoryStream):Boolean; property Next:TVariableData read fNext write SetNext; property Pre :TVariableData read fPre write SetPre; end; //全データをメモリに保持する方式 //ハードディスクへの書き込みはリアルタイム TVariableDataList = class(TObject) private LastDataPos,FirstDataPos:Integer; public MaxSize:Integer; //ディスクに格納できる最大サイズ fFileName:string; FirstData,LastData: TVariableData; constructor Create; function AddData(str:string):Integer; procedure Clear; procedure RemoveMinimum; procedure LoadFromFile(aFileName:string); end; TForm1 = class(TForm) Button1: TButton; ListView1: TListView; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private 宣言 } fDataFile:string; VariableDatList: TVariableDataList; public { Public 宣言 } procedure ListUp; end; var Form1: TForm1; implementation {$R *.dfm} { TVariableData } function TVariableData.LoadFromStream(aStream: TMemoryStream):Boolean; var strLen:Integer; begin if aStream.Position + SizeOf(Integer)*2 > aStream.Size then begin Result := False; Exit; end; aStream.Read(ID,Sizeof(Integer)); aStream.Read(strLen,Sizeof(Integer)); if aStream.Position + strLen*2 > aStream.Size then begin Result := False; Exit; end; SetLength(Str,strLen); aStream.Read(Str[1],strLen*2); Result := True; end; procedure TVariableData.SaveToStream(aStream: TMemoryStream); var strLen:Integer; begin aStream.Write(ID,Sizeof(Integer)); strLen := Length(Str); aStream.Write(strLen,Sizeof(Integer)); aStream.Write(Str[1],strLen*2); end; procedure TVariableData.SetNext(const Value: TVariableData); begin if Value<>nil then Value.fPre := Self; fNext := Value; end; procedure TVariableData.SetPre(const Value: TVariableData); begin if Value<>nil then Value.fNext := Self; fPre := Value; end; function TVariableData.Size: Integer; begin Result := Sizeof(Integer)*2 + Length(Str)*2; end; procedure TVariableData.WriteFile(aStream: TFileStream); var strLen:Integer; begin aStream.Write(ID,Sizeof(Integer)); strLen := Length(Str); aStream.Write(strLen,Sizeof(Integer)); aStream.Write(Str[1],strLen*2); end; { TVariableDataList } function TVariableDataList.AddData(str: string): Integer; var aData:TVariableData; aSize,aSize2:Integer; aFileStream:TFileStream; begin aData := TVariableData.Create; aData.Str := str; if FirstData=nil then begin FirstData := aData; aData.ID := 0; end else aData.ID := LastData.ID +1; Result := aData.ID; aData.Pre := LastData; LastData := aData; aFileStream := TFileStream.Create(fFileName,fmOpenReadWrite); aSize := aData.Size; if (LastDataPos < MaxSize) and (LastDataPos >= FirstDataPos) then begin //最大サイズ以下の場合 aFileStream.Position := LastDataPos + SizeOf(Integer)*2; aData.WriteFile(aFileStream); LastDataPos := LastDataPos + aSize; end else begin //最大サイズ超過 aSize2 := 0; repeat aSize2 := aSize2 + FirstData.Size; RemoveMinimum; until aSize2 >= aSize; FirstDataPos:= FirstDataPos + aSize2; if FirstDataPos >= MaxSize then FirstDataPos := 0; if LastDataPos >= MaxSize then LastDataPos := 0; aFileStream.Position := LastDataPos + SizeOf(Integer)*2; aData.WriteFile(aFileStream); LastDataPos := LastDataPos + aSize; end; aFileStream.Position:=0; aFileStream.Write(FirstDataPos,SizeOf(Integer)); aFileStream.Write(LastDataPos,SizeOf(Integer)); aFileStream.Free; end; procedure TVariableDataList.Clear; var aData1,aData2:TVariableData; begin aData1 := FirstData; while aData1<>nil do begin aData2 := aData1.Next; aData1.Free; aData1 := aData2; end; FirstData := nil; LastData := nil; FirstDataPos:= 0; LastDataPos := 0; end; constructor TVariableDataList.Create; begin inherited; fFileName := 'data.dat'; FirstData := nil; LastData := nil; FirstDataPos:= 0; LastDataPos := 0; MaxSize := 256*2*10; //ディスクに格納できる最大サイズ end; procedure TVariableDataList.LoadFromFile(aFileName: string); var aStream:TMemoryStream; aData,aDataPre:TVariableData; aCanLoad:Boolean; begin Clear; fFileName := aFileName; if FileExists(aFileName)=False then begin aStream := TMemoryStream.Create; aStream.SaveToFile(aFileName); aStream.Free; Exit; end; aStream := TMemoryStream.Create; aStream.LoadFromFile(aFileName); aStream.Read(FirstDataPos,SizeOf(Integer)); aStream.Read(LastDataPos,SizeOf(Integer)); aStream.Position:=FirstDataPos + SizeOf(Integer)*2; aDataPre := nil; repeat aData := TVariableData.Create; aCanLoad := aData.LoadFromStream(aStream); if aCanLoad then begin aData.Pre := aDataPre; aDataPre := aData; if FirstData = nil then FirstData := aData; LastData := aData; end; until (aCanLoad=False); aStream.Position := SizeOf(Integer)*2; if LastDataPos < FirstDataPos then repeat aData := TVariableData.Create; aCanLoad := aData.LoadFromStream(aStream); if aCanLoad then begin aData.Pre := aDataPre; aDataPre := aData; LastData := aData; end; until aStream.Position + SizeOf(Integer)*2 >= LastDataPos; aStream.Free; end; procedure TVariableDataList.RemoveMinimum; begin FirstData := FirstData.next; FirstData.pre.Free; FirstData.pre:=nil; end; procedure TForm1.Button1Click(Sender: TObject); var i: Integer; begin for i := 0 to 100 - 1 do begin VariableDatList.AddData(DateTimeToStr(Now)+ ' - ' +IntToStr(i)); end; ListUp; end; procedure TForm1.FormCreate(Sender: TObject); begin VariableDatList := TVariableDataList.Create; fDataFile := ExtractFilePath(Application.ExeName)+'test.dat'; VariableDatList.LoadFromFile(fDataFile); ListUp; end; procedure TForm1.ListUp; var item:TListItem; aData: TVariableData; begin ListView1.Clear; aData := VariableDatList.FirstData; while aData <> nil do begin item := ListView1.Items.Add; item.Caption := IntToStr(aData.ID); item.SubItems.Add(aData.Str); aData := aData.Next; end; end; end.
←解決時は質問者本人がここをチェックしてください。
更新する
戻る
掲示板システム
Copyright 2021 Takeshi Okamoto All Rights Reserved.