2013-10-03 32 views
4

過去,我已經看到了這項工作,但我從來沒有真正理解應該如何完成。
假設我們有一個文件已知的數據類型的,但未知長度,像TSomething動態陣列,其中有效使用Delphi從文件中讀取未知大小的塊

type 
    TSomething = class 
    Name: String; 
    Var1: Integer; 
    Var2: boolean; 
    end; 

的問題,不過,是該對象類型可以被擴展未來,增加更多變量(例如Var3: String)。
然後,用舊版本保存的文件將不包含最新變量
文件讀取過程應該以某種方式識別數據,與像一個算法:

procedure Read(Path: String) 
begin 
    // Read Array Size 
    // Read TSomething --> where does this record end? May not contain Var3! 
    // --> how to know that the next data block I read is not a new object? 
end; 

我已經看到了BlockReadBlockWrite這個工作,我認爲每一個對象應該寫自己之前可能會寫它的大小在文件中,但我會感激一個例子(不一定是代碼),知道我正在朝着正確的方向思考。

相關的讀數,我發現:
SO - Delphi 2010: How to save a whole record to a file?
Delphi Basics - BlockRead
SO - Reading/writing dynamic arrays of objects to a file - Delphi
SO - How Can I Save a Dynamic Array to a FileStream in Delphi?

回答

7

爲了使這項工作,你需要編寫的單元尺寸的文件。然後,當你閱讀文件時,即使你的程序不知道如何理解所有的元素,你也可以閱讀該元素的長度,以便讀取每個元素。

關於將您的記錄與磁盤上的記錄進行匹配,如果您的記錄只包含簡單類型,那麼這很容易。在這種情況下,你可以從文件Min(ElementLength, YourRecordSize)讀入你的記錄。

但它看起來好像你真的有這種情況。你的記錄實際上是一個班級,因此不適合記憶複製。更重要的是,它的第一個成員是一個絕對不是簡單類型的字符串。

回到當天(比如說20世紀70年代),您描述的技術是如何讀取文件。但是現在編程已經開始了。將結構化數據保存到文件通常意味着使用更靈活和適應性更強的序列化格式。您應該考慮使用JSON,XML,YAML或類似的任務。

+0

謝謝大衛。這是客觀的,但我認爲你解決了閱讀未知尺寸和序列化。謝謝。 – mavrosxristoforos

1

如果我的理解正確,您的主要問題是如果TSomething更改。最重要的是,您需要將版本信息添加到您的文件中,這是您無法避免的。

至於實際存儲使用Sqlite很可能會解決您的所有問題,但根據您的情況,它可能是一個矯枉過正。

除了普通的情況,我不會擔心擴展類太多。如果你添加版本號到文件的開頭,你可以在類更改後輕鬆地轉換文件。您所需要做的就是實施您的解決方案,以便添加轉換儘可能簡單。

爲了讀取/寫入文件,我寧願使用streams/XML/JSON(取決於情況)而不是blockread/blockwrite,因爲您不必實施hack來存儲版本號。

理論上你也可以爲每條記錄留有未使用的空間,所以我可以避免重新創建整個文件,如果類改變到某一點(直到你有足夠的未使用空間)。如果TSomething經常更改並且文件很大,這可能會有幫助,但最有可能我不會走這條路線。

+0

感謝您的建議。我不會爲這樣的小任務使用SQL,但我肯定會考慮將它轉換爲XML或JSON。 – mavrosxristoforos

5

我想說你需要一個版本化你的文件的方法。這樣你就知道文件中包含了哪個版本的記錄。在文件開始處寫入,然後在讀取時,先讀入版本標識符,然後使用相應的結構讀取其餘結構。

+0

這是我使用的方法,它工作得很好。包含用於標識文件類型的ID的標頭,用於標識文件佈局和使用的記錄類型的版本號以及用於控制記錄數據解釋而不影響的額外標誌如何讀取數據(字符編碼,功能等)。 –

+0

這不會讓舊程序讀取新文件 –

+0

@David,所以如果您完全改變了所使用結構的整體含義,您認爲舊程序需要知道文件中的數據是什麼?我不。我只是告訴用戶他運氣不好,他應該得到我的程序的更新版本。 – TLama

1

這是我該怎麼做的:在標題中包含一個簡單的版本號。這可以是任何字符串,整數或其他。

讀取和寫入該文件是很容易的(我使用僞代碼):

Procedure Read (MyFile : TFile); 
Var 
    reader : IMyFileReader; 

begin 
    versionInfo = MyFile.ReadVersionInfo(); 
    reader = ReaderFactory.CreateFromVersion(versionInfo); 
    reader.Read(MyFile); 
end; 


Type 
    ReaderFactory = Class 
    public 
    class function CreateFromVersion(VersionInfo : TVersionInfo) : IMyFileReader; 
    end; 

function ReaderFactory.CreateFromVersion(VersionInfo : TVersionInfo) : IMyFileReader; 
begin 
    if VersionInfo = '0.9-Alpha' then 
    result := TVersion_0_9_Alpha_Reader.Create() 
    else if VersionInfo = '1.0' then 
    result := TVersion1_0_Reader.Create() 
    else .... 
end; 

這可以很容易地維護和延長下去。您永遠不必觸摸Read-routine,但只需添加新的閱讀器並增強工廠。通過簡單的註冊方法和TDictionary<TVersionInfo,TMyFileReaderClass>,您甚至可以避免修改工廠。

+0

這與Pieter B的答案相同,並且具有舊程序無法讀取新文件的相同問題。 –

+1

如果沒有辦法區分版本化文件和未版本化文件,那麼沒有舊的程序能夠讀取版本化文件。您可以開始將版本信息添加到您的文件中,或者從一開始就使用結構化文件格式,否則您會卡住。 – alzaimar

+0

瞭解該文件是不同的格式不允許您閱讀它。使用結構化格式(可以是JSON,XML,或者可以是家庭brew二進制格式,或者可以是其他許多事物)確實允許這樣做。無論如何,你的評論與我的一致。 –