2016-04-03 54 views
1

我試圖使用編碼/ gob將數據存儲到文件並稍後加載。我希望能夠將新數據附加到文件並稍後加載所有保存的數據,例如,重新啓動我的應用程序後。使用Encode()存儲到文件中時沒有問題,但在閱讀時似乎總是隻獲取首次存儲的項目,而不是簡潔的存儲項目。通過追加多次檢索寫入文件的gob

下面是一個小例子:https://play.golang.org/p/patGkKDLhM

正如你看到的,它的工作原理寫兩次的編碼器,然後讀出來。但是,當關閉文件並以附加模式重新打開文件時,寫入似乎可行,但讀取僅適用於前兩個元素(先前已寫入)。這兩個新增加的結構無法找回,我得到的錯誤:

panic: extra data in buffer

我知道Append to golang gob in a file on disk,我也讀https://groups.google.com/forum/#!topic/golang-nuts/bn6vjC5Abd8

最後,我還發現https://gist.github.com/kjk/8015952這似乎證明什麼,我想做不起作用。爲什麼?這個錯誤是什麼意思?

回答

1

我還沒有使用encoding/gob軟件包(看起來很酷,我可能不得不爲它找一個項目)。但是閱讀godoc,在我看來,每個編碼都是一個單獨的記錄,預計將從頭到尾進行解碼。也就是說,一旦你的流Encode,結果字節是一個完整的集合,從整個流程開始到結束 - 不能通過再次編碼添加到後面。

Godoc聲稱編碼的gob是自描述性的。在編碼流的開頭,它描述了包括字段名稱在內的整個數據集結構,類型等。然後,字節流中的內容是這些導出字段的值的大小和字節表示。

那麼可以假設,什麼是從文檔中省略是因爲流的自我描述自己在開始的時候,包括即將被傳遞各個領域,這是所有的Decoder會在意。 Decoder將不知道在描述了什麼之後添加的任何連續的字節,因爲它只能看到開始時描述的內容。因此,該錯誤消息panic: extra data in buffer是準確的。

在您的Playground示例中,您將對相同的編碼器實例編碼兩次,然後關閉該文件。由於您正在傳遞兩條記錄並對兩條記錄進行編碼,因此可能會工作,因爲編碼器的單個實例可能會將兩個Encode調用視爲單個編碼流。然後,當關閉文件io的流時,gob現在已完成 - 並且流被視爲單個記錄(即使您以兩種類型發送)。

與解碼功能相同,您從同一個流中讀取X次。但是,在關閉文件時您正在編寫單個記錄 - 實際上在該單個記錄中有兩種類型。因此,爲什麼它在讀取2和2時工作。但如果讀取的數量超過2,則會失敗。

如果要將它存儲在單個文件中,則需要爲每個解決方案創建自己的索引完成「寫入」或編碼器實例/會話。有些形成自己的Block方法,允許用「開始」和「結束」標記來包裝或定義寫入磁盤的每個條目。這樣,當讀迴文件時,由於開始/結束標記,您確切知道要分配哪個緩衝區。一旦在緩衝區中有單個記錄,則使用gob的Decoder對其進行解碼。每次寫入後關閉文件。

我使用的這類標記的圖案是這樣的:

uint64:uint64 
uint64:uint64 
... 

第一個是開頭字節數,以冒號是其長度分開的所述第二條目。我通常會將其存儲在另一個文件中,並稱爲indexes。這樣它可以快速讀入內存,然後我可以流大文件,確切知道每個開始和結束地址在字節流中的位置。

另一種選擇是將每個gob存儲在其自己的文件中,使用文件系統目錄結構進行組織(例如,甚至可以使用目錄來定義類型)。那麼每個文件的存在就是一條記錄。這就是我如何使用事件採購技術中呈現的json,存儲數百萬個目錄中組織的文件。

總之,我認爲數據是一個完整的數據集,從頭至尾 - 只有一個「記錄」。如果你想存儲多個編碼/多個gobs,那麼需要創建自己的索引來跟蹤每個gob字節的起始和大小/結束。然後,您將分別想要每個條目Decode

+0

謝謝,這是有道理的。另一個選項應該是在連續寫入之間不要關閉編碼器。這應該工作,直到應用程序關閉。 – alex

+1

是的,不關閉會起作用。 Gob的特殊之處在於它遇到的每一種新類型都會寫一個描述記錄來定義字段和類型。通過創建新的編碼器,它會丟失跟蹤哪些類型已被定義,因此它在第二次寫入時開始再次定義它們。我認爲,這會混淆解碼器。 –

+0

這幾乎就像他們錯過了一個「關閉」方法來表明它已完成。 – eduncan911