2016-11-12 25 views
-1

複数の文字列リストを保存するためのユニットを作成しました。各TStrings項目は、テキストとオブジェクトを表す整数値を含むレコードとして格納されます。全体がバイナリファイルに書き込まれます。以下は、データを書き込むルーチンです。文字列リストの名前をパラメータとして渡すにはどうすればいいですか?

function AddToStream(Stream: TStream; Const pList: TStringList):Boolean; 
Var idy: Integer; 
    TmpItem: tItemRec; 
begin 
    TmpItem.pText := pList.ClassName;  // Set up the Header 
    TmpItem.pObj := pList.Count * SizeOf(TmpItem); // Calc the # bytes for Stringlist 
    Stream.WriteBuffer(TmpItem, SizeOf(TmpItem)); // Write it to the Stream 
    for idy := 0 to plist.Count -1 do begin   // Cycle through StringList 
     TmpItem.pText := pList[idy];     // Get the Text part 
     TmpItem.pObj := Integer(pList.Objects[idy]); // Get the Object part 
     Stream.WriteBuffer(TmpItem, SizeOf(TmpItem)); // Write record to stream 
    end; 
end; 

ストリームに書き込まれる最初のレコードは、後続のファイルの文字列リストとバイト数を識別する名前を持つことを目的としています。 ClassNameが上記のコードでTStringListを返すことは明らかです。渡された文字列リストの変数名、つまりMyStringListを取得するにはどうすればいいですか?

渡された標準の文字列リストからそれを派生させることは可能ですか、またはstringlistをサブクラス化して、VariableNameプロパティをリストに追加する必要がありますか。

私のコードをすべて表示しているはずです。私のオリジナルの問題とは別に、私は少なくともTStringLists 1つで動作するコードを持っていると思います。命名の問題を何をすべきかを決定するまで、私は複数の文字列リストをテストしていません。以下は完全な単位です。

unit MultiFileUtils; 

interface 

Uses 
System.SysUtils, System.Variants, System.Classes, Vcl.Dialogs, system.UITypes; 


{This unit enables Multiple stringlist to be saved with objects to a single file 
and reloaded the into the stringlists retaining their originla object value. 
The stringlists you reload to should have classname as the stringlist you saved from 
The data is held in a binary file, each string list has a aheader which holds 
the ClassName of the stringlist and the length of the file. The text portion 
of each entry in the stringlist should not exceed 255 characters. 

Save functions return true if OK, AllowOverWrite doesn't check file already exists. 
Read function returns true if OK, false if file not found or classname not found in file} 

Function SaveLists(Const pLists: Array of TStringList; const pFileName: String; AllowOverwrite: Boolean): Boolean; 
Function SaveList(Const pList: TStringList; const pFileName: String; AllowOverwrite: Boolean):Boolean; 
Function ReadList(Const pFileName: String; Var pList: TStringList): Boolean; 

procedure LoadTestData; 
procedure SetUpTests; 
procedure TestSave; 
procedure TestRead; 
Procedure ClearTests; 

implementation 


Type 
    tItemRec = record 
    pText: String[255]; 
    pObj: Integer; 
    end; 

{$ifDef Debug} 
Var StrList1: TStringlist; 
    StrList2: TStringlist; 
{$EndIf} 

function CheckFileExists(pFileName: String):Boolean; 
begin 
    if FileExists(pFileName) then 
    Result := (MessageDlg(pFileName + ' already exists, do you want to overwrite file?', 
          mtConfirmation, [mbYes,mbNo],0) = mrYes); 
end; 

function AddToStream(Stream: TStream; Const pList: TStringList):Boolean; 
Var 
    idy: Integer; 
    TmpItem: tItemRec; 
begin 
    TmpItem.pText := pList.ClassName;    // Set up the Header 
    TmpItem.pObj := pList.Count * SizeOf(TmpItem); // Calc the # bytes for Stringlist 
    Stream.WriteBuffer(TmpItem, SizeOf(TmpItem)); // Write it to the Stream 
    for idy := 0 to plist.Count -1 do begin   // Cycle through StringList 
    TmpItem.pText := pList[idy];     // Get the Text part 
    TmpItem.pObj := Integer(pList.Objects[idy]); // Get the Object part 
    Stream.WriteBuffer(TmpItem, SizeOf(TmpItem)); // Write record to stream 
    end; 
end; 

function SaveLists(Const pLists: Array of TStringList; Const pFileName: String; 
        AllowOverwrite: Boolean): Boolean; 
Var 
    idx: Integer; 
    Stream: TStream; 
begin 
    if AllowOverwrite then 
    Result := true 
    else 
    Result := CheckFileExists(pFileName); 
    if Result then begin 
    Stream := TFileStream.Create(pFileName, fmCreate); // Set up a fileStream 
    try 
     for idx := 0 to Length(plists) do   // Loop through array of stringlists 
     AddToStream(Stream, pLists[idx]);   // Add each Stringlist 
    finally 
     Stream.Free;        // Write to disk and free Stream 
    end; 
    end; 
end; 

function SaveList(Const pList: TStringList; const pFileName: String; 
        AllowOverwrite: Boolean): Boolean; 
Var 
    idx: Integer; 
    Stream: TStream; 
begin 
    If AllowOverwrite then 
    result := true 
    else 
    Result := CheckFileExists(pFileName); 
    if Result then begin 
    Stream := TFileStream.Create(pFileName, fmCreate); // Set up filestream 
    try 
     AddToStream(Stream, pList);     // Add Stringlist to stream 
    finally 
     Stream.Free;        // Write to disk and free Stream 
    end; 
    end; 
end; 

function ReadList(Const pFileName: String; var pList: TStringList): Boolean; 
Var idx: Integer; 
    Stream: TStream; 
    TmpItem: tItemRec; 

    Function NotEos: Boolean; 
    begin 
    Result := Stream.Position < Stream.Size; 
    end; 

begin 
    Result := false; 
    if FileExists(pFileName) then begin 
    Stream := TFileStream.Create(pFileName, fmOpenRead); 
    Stream.Seek(0, soBeginning); 
    while NotEos do begin 
     if Stream.Read(TmpItem, SizeOf(TmpItem)) = SizeOf(TmpItem) then // Read Header 
     if TmpItem.pText = pList.ClassName then begin 
      Result := True;       // Found header so file looks OK 
      idx := TmpItem.pObj;     // Get the byte count 
      while (idx > 0) And NotEos do begin 
      Stream.ReadBuffer(TmpItem, SizeOf(TmpItem)); 
      pList.AddObject(Trim(TmpItem.pText), Pointer(TmpItem.pObj)); 
      Dec(idx); 
      end; 
      break; 
     end; 
    end; 
    Stream.Free; 
    end; 
end; 

{$ifDef Debug} 
Procedure LoadTestData; 
Var i: Integer; 
begin 
    for i := 0 to 20 do begin 
    StrList1.AddObject('StrLst1 Data' + IntToStr(i), Pointer(i+1000)); 
    StrList2.AddObject('StrLst2 Data' + IntToStr(i), pointer(i+2000)); 
    end; 
end; 

procedure SetUpTests; 
begin 
    StrList1 := TStringList.Create; 
    StrList2 := TStringList.Create; 
    LoadTestData; 
end; 

Procedure TestSave; 
begin 
    SaveList(StrList1, 'MyTestFile.dat', true); 
end; 

Procedure TestRead; 
begin 
    StrList1.Clear; 
    ReadList('MyTestFile.dat', StrList1); 
end; 

procedure ClearTests; 
begin 
    StrList1.Free; 
    StrList2.Free; 
end; 
{$endif} 

end. 
+0

'tItemRec'定義を表示します。コンパイルされたexeには変数名が含まれていません。説明してください。検索中にどのように名前情報を使用しますか? – MBo

+0

変数名はコンパイルされませんが、コンパイラは実行可能コードでそれらを必要としません。彼らは人間のためのものです。 –

+0

ところで第2段落は、あなたがストリームにクラス名を書いていると思うかもしれません。あなたはそうではありません、ストリームには文字列とオブジェクトのアドレスが含まれています。 –

答えて

3

文字列リストインスタンスには名前がありません。変数名はプログラムで取得することはできません。たとえ呼び出し元サイトの変数が関数内にあるときに失われる可能性があります。あなたがしようとしていることは不可能です。

私の見解では、名前を含む関数に余分な引数を渡すのが一番きれいです。名前を追加する派生クラスを等しく使用できますが、このコードのコンシューマーは派生クラスを使用するように制限されます。

文字列リストを書き込むコードを見ると、非常に壊れています。あなたはメモリの内容ではなくメモリアドレスを書き込んでいるようです。しかしそれは別の問題です。

+0

私は、オブジェクトインスタンスが持つ可能な名前ではなく、インスタンスに割り当てられた変数の名前について質問することを理解しています。私は間違っているかもしれません。 –

+0

@sertacあなたはaskerが値 'pList'を持つ文字列を望んでいると思いますか? –

+0

それとも、彼がコールサイトのリストと呼ぶか。それが私がそれを理解した方法です。 –